From 65ecb378ebe558f92ea91d3fe50bfdc2cbb31a86 Mon Sep 17 00:00:00 2001 From: Qi Xiao Date: Mon, 19 Nov 2018 11:29:04 +0800 Subject: [PATCH 01/24] Update deps Using new go-mysql implementation which support authentication with multiple users --- Gopkg.lock | 62 +++++++------- vendor/github.com/alecthomas/chroma/README.md | 27 +++++- .../alecthomas/chroma/formatters/html/html.go | 37 ++++++--- .../alecthomas/chroma/lexers/b/bash.go | 2 +- .../alecthomas/chroma/lexers/e/elixir.go | 6 +- .../alecthomas/chroma/lexers/g/go.go | 1 + .../alecthomas/chroma/lexers/h/http.go | 1 + .../alecthomas/chroma/lexers/s/systemd.go | 29 +++++++ .../alecthomas/chroma/lexers/t/tradingview.go | 36 ++++++++ vendor/github.com/fortytw2/leaktest/README.md | 18 ++-- .../github.com/fortytw2/leaktest/leaktest.go | 5 +- vendor/github.com/jtolds/gls/stack_tags.go | 34 ++++++++ vendor/github.com/juju/errors/error.go | 4 + vendor/github.com/miekg/dns/client.go | 83 +------------------ vendor/github.com/miekg/dns/server.go | 34 ++++++-- vendor/github.com/miekg/dns/version.go | 2 +- .../client_golang/prometheus/counter.go | 2 +- .../client_golang/prometheus/gauge.go | 2 +- .../client_golang/prometheus/histogram.go | 2 +- .../client_golang/prometheus/labels.go | 21 ++++- .../client_golang/prometheus/registry.go | 36 ++++++++ .../client_golang/prometheus/summary.go | 2 +- .../siddontang/go-mysql/server/auth.go | 8 +- .../siddontang/go-mysql/server/conn.go | 23 ++++- .../syndtr/goleveldb/leveldb/cache/cache.go | 1 - .../leveldb/comparer/bytes_comparer.go | 4 +- .../github.com/syndtr/goleveldb/leveldb/db.go | 2 +- .../syndtr/goleveldb/leveldb/db_util.go | 2 +- .../syndtr/goleveldb/leveldb/session_util.go | 4 +- .../syndtr/goleveldb/leveldb/table.go | 4 +- vendor/github.com/xo/dburl/README.md | 2 +- vendor/github.com/xo/dburl/dsn.go | 3 +- vendor/github.com/xo/dburl/go.sum | 0 vendor/github.com/xo/tblfmt/go.mod | 5 +- vendor/github.com/xo/tblfmt/go.sum | 2 - .../golang.org/x/net/internal/socket/empty.s | 7 ++ .../net/internal/socket/sys_go1_11_darwin.go | 33 ++++++++ .../net/internal/socket/sys_go1_12_darwin.go | 42 ++++++++++ .../x/net/internal/socket/sys_posix.go | 36 +++++--- .../x/net/internal/socket/sys_unix.go | 2 +- vendor/golang.org/x/net/ipv4/doc.go | 3 +- vendor/golang.org/x/net/ipv4/payload_cmsg.go | 2 +- .../x/net/ipv4/payload_cmsg_go1_8.go | 2 +- .../x/net/ipv4/payload_cmsg_go1_9.go | 2 +- .../golang.org/x/net/ipv4/payload_nocmsg.go | 2 +- vendor/golang.org/x/net/ipv6/doc.go | 3 +- vendor/golang.org/x/net/ipv6/payload_cmsg.go | 2 +- .../x/net/ipv6/payload_cmsg_go1_8.go | 2 +- .../x/net/ipv6/payload_cmsg_go1_9.go | 2 +- .../golang.org/x/net/ipv6/payload_nocmsg.go | 2 +- .../golang.org/x/sys/unix/asm_linux_ppc64x.s | 12 --- vendor/golang.org/x/sys/unix/mkerrors.sh | 2 +- vendor/golang.org/x/sys/unix/mkpost.go | 8 ++ vendor/golang.org/x/sys/unix/syscall_linux.go | 63 ++++++++++++++ .../golang.org/x/sys/unix/syscall_netbsd.go | 8 ++ .../golang.org/x/sys/unix/syscall_unix_gc.go | 2 +- .../x/sys/unix/syscall_unix_gc_ppc64x.go | 24 ++++++ vendor/golang.org/x/sys/unix/types_freebsd.go | 2 +- vendor/golang.org/x/sys/unix/types_netbsd.go | 2 + .../x/sys/unix/ztypes_freebsd_386.go | 2 +- .../x/sys/unix/ztypes_freebsd_amd64.go | 2 +- .../x/sys/unix/ztypes_freebsd_arm.go | 2 +- .../golang.org/x/sys/unix/ztypes_linux_386.go | 3 + .../x/sys/unix/ztypes_linux_amd64.go | 3 + .../golang.org/x/sys/unix/ztypes_linux_arm.go | 3 + .../x/sys/unix/ztypes_linux_arm64.go | 3 + .../x/sys/unix/ztypes_linux_mips.go | 3 + .../x/sys/unix/ztypes_linux_mips64.go | 3 + .../x/sys/unix/ztypes_linux_mips64le.go | 3 + .../x/sys/unix/ztypes_linux_mipsle.go | 3 + .../x/sys/unix/ztypes_linux_ppc64.go | 3 + .../x/sys/unix/ztypes_linux_ppc64le.go | 3 + .../x/sys/unix/ztypes_linux_riscv64.go | 3 + .../x/sys/unix/ztypes_linux_s390x.go | 3 + .../x/sys/unix/ztypes_netbsd_386.go | 7 ++ .../x/sys/unix/ztypes_netbsd_amd64.go | 7 ++ .../x/sys/unix/ztypes_netbsd_arm.go | 7 ++ 77 files changed, 614 insertions(+), 220 deletions(-) create mode 100644 vendor/github.com/alecthomas/chroma/lexers/s/systemd.go create mode 100644 vendor/github.com/alecthomas/chroma/lexers/t/tradingview.go create mode 100644 vendor/github.com/xo/dburl/go.sum create mode 100644 vendor/golang.org/x/net/internal/socket/empty.s create mode 100644 vendor/golang.org/x/net/internal/socket/sys_go1_11_darwin.go create mode 100644 vendor/golang.org/x/net/internal/socket/sys_go1_12_darwin.go create mode 100644 vendor/golang.org/x/sys/unix/syscall_unix_gc_ppc64x.go diff --git a/Gopkg.lock b/Gopkg.lock index dca93369b..c0081f6fc 100644 --- a/Gopkg.lock +++ b/Gopkg.lock @@ -53,7 +53,7 @@ revision = "b72b1571892216ba2a42fb8abeb726d082b4c4d1" [[projects]] - digest = "1:9253f97cfbbe049b631877c80badecc69620711b3e335f6cf97a7809681da388" + digest = "1:26777bf96d1c96d9be38c7c9795196b088c91c597ec5dae5bf6c02dc3c3520e9" name = "github.com/alecthomas/chroma" packages = [ ".", @@ -89,8 +89,8 @@ "styles", ] pruneopts = "UT" - revision = "5d7fef2ae60b501bbf28d476c3f273b8017d8261" - version = "v0.5.0" + revision = "5a473179cfe450c6d80407452c241428de387f6b" + version = "v0.6.0" [[projects]] digest = "1:a8d622a8049a4aa420e1c509873bb85d4c45c5107f420d922f919bfcb8d08694" @@ -168,12 +168,12 @@ revision = "816b6608b3c8c1e871bc9cf777f390e2532081fe" [[projects]] - digest = "1:544229a3ca0fb2dd5ebc2896d3d2ff7ce096d9751635301e44e37e761349ee70" + digest = "1:547f5df5f708c880f0af507317c8d022b8d62053a5cb075e23c6eeed09eb2e4e" name = "github.com/fortytw2/leaktest" packages = ["."] pruneopts = "UT" - revision = "a5ef70473c97b71626b9abeda80ee92ba2a7de9e" - version = "v1.2.0" + revision = "9a23578d06a26ec1b47bfc8965bf5e7011df8bd6" + version = "v1.3.0" [[projects]] branch = "master" @@ -205,7 +205,7 @@ name = "github.com/gopherjs/gopherjs" packages = ["js"] pruneopts = "UT" - revision = "0766667cb4d1cfb8d5fde1fe210ae41ead3cf589" + revision = "d547d1d9531ed93dbdebcbff7f83e7c876a1e0ee" [[projects]] digest = "1:c79fb010be38a59d657c48c6ba1d003a8aa651fa56b579d959d74573b7dff8e1" @@ -248,20 +248,20 @@ revision = "4896c6bd164172da9f78789f233b470a3a6fb359" [[projects]] - digest = "1:1e15f4e455f94aeaedfcf9c75b3e1c449b5acba1551c58446b4b45be507c707b" + digest = "1:4b63210654b1f2b664f74ec434a1bb1cb442b3d75742cc064a10808d1cca6361" name = "github.com/jtolds/gls" packages = ["."] pruneopts = "UT" - revision = "77f18212c9c7edc9bd6a33d383a7b545ce62f064" - version = "v4.2.1" + revision = "b4936e06046bbecbb94cae9c18127ebe510a2cb9" + version = "v4.20" [[projects]] branch = "master" - digest = "1:cdc32003a67007f608c767cfbea5b7a07ed7418ec9aed579707e3185086a6878" + digest = "1:7d1a655e1f16ccaf1adb026bdeece28499ecb0ead32d32accc6ed6f2c40cacee" name = "github.com/juju/errors" packages = ["."] pruneopts = "UT" - revision = "a4583d0a56eac4bdd939e38050d0c69abe0a9b41" + revision = "089d3ea4e4d597bd98acac068193d341983326a3" [[projects]] branch = "master" @@ -312,12 +312,12 @@ version = "v1.0.1" [[projects]] - digest = "1:c22d250c583ec1136ecad8b74a43f408c08ddbbcbae715341ec4a49f332628e0" + digest = "1:b20d8767957e40b9302a8caaee88caa698513fbd972509a54555302c3aebfa04" name = "github.com/miekg/dns" packages = ["."] pruneopts = "UT" - revision = "915ca3d5ffd945235828a097c917311a9d86ebb4" - version = "v1.0.14" + revision = "7064f7248f5fa5fd79382a76328b4e200b79e4ae" + version = "v1.0.15" [[projects]] branch = "master" @@ -345,14 +345,14 @@ [[projects]] branch = "master" - digest = "1:f696f304d2a14745859a153f1041b66e0e2cf150eff731beb6431e93e27ddc5c" + digest = "1:735956b5e94182229dfbdc5b401d7a71f20f54f1259dbc2969ec2b56bfbb34f2" name = "github.com/prometheus/client_golang" packages = [ "prometheus", "prometheus/internal", ] pruneopts = "UT" - revision = "f30f428035633da15d00d3dfefb0128c5e569ef4" + revision = "3fb53dff765f8a3e0f9d8b1d5b86d4f8c4eb3a09" [[projects]] branch = "master" @@ -373,7 +373,7 @@ "version", ] pruneopts = "UT" - revision = "7e9e6cabbd393fc208072eedef99188d0ce788b6" + revision = "1f2c4f3cd6db5fd6f68f36af6b6d5d936fd93c4e" [[projects]] branch = "master" @@ -428,7 +428,7 @@ [[projects]] branch = "master" - digest = "1:d3384958d1ace51e2187c9747d806eee27d1aebdb1a4df798058a8cacc19b379" + digest = "1:25913ccf8c947d80f59e211b41225a85bd8e491f22b61a8c7e04f5774b2c01d3" name = "github.com/siddontang/go-mysql" packages = [ "mysql", @@ -436,7 +436,7 @@ "server", ] pruneopts = "UT" - revision = "7f644882a1f315e77bfda60b4f689a45a2eec228" + revision = "d8fba0f23b9b3abe91c6f01842c72edd925ccb93" source = "github.com/CovenantSQL/go-mysql" [[projects]] @@ -473,7 +473,7 @@ [[projects]] branch = "master" - digest = "1:59483b8e8183f10ab21a85ba1f4cbb4a2335d48891801f79ed7b9499f44d383c" + digest = "1:9ff9e1808adfc43f788f1c1e9fd2660c285b522243da985a4c043ec6f2a2d736" name = "github.com/syndtr/goleveldb" packages = [ "leveldb", @@ -490,7 +490,7 @@ "leveldb/util", ] pruneopts = "UT" - revision = "6b91fda63f2e36186f1c9d0e48578defb69c5d43" + revision = "f9080354173f192dfc8821931eacf9cfd6819253" [[projects]] digest = "1:d9eb1d964a449cf51455fdac2ec3451c664af8e8b93c23d9c31d22b84ea82fde" @@ -518,19 +518,19 @@ [[projects]] branch = "master" - digest = "1:4a2baa112d6e7d222e78fc4702796592a47c5a6341e34e9b0ee39582e32cdf99" + digest = "1:cbe0326a31e9351aa78c5ae05e90ee99beb7f1f9b4f0aa9438d8174cc91a08aa" name = "github.com/xo/dburl" packages = ["."] pruneopts = "UT" - revision = "e33971d4c1320eca4c3585875e410c1eeadc4c3a" + revision = "f3ca07bd6afccbc084c872c7b49d666f50d0a1f6" [[projects]] branch = "master" - digest = "1:3863946ace74cbd518a33a2e8dd145a13aba1fc37a0e89788154083f183fca11" + digest = "1:2f33ea228f93fce4751f285a233ca262f51afa47336a1695f8967c9e83a1f42d" name = "github.com/xo/tblfmt" packages = ["."] pruneopts = "UT" - revision = "e82d5611beb24993cc6afc59cdacf6e598891d12" + revision = "0b109994b29d40a7e603c678b3d6823dbc0a1309" [[projects]] branch = "master" @@ -584,11 +584,11 @@ "ssh/terminal", ] pruneopts = "UT" - revision = "4d3f4d9ffa16a13f451c3b2999e9c49e9750bf06" + revision = "3d3f9f413869b949e48070b5bc593aa22cc2b8f2" [[projects]] branch = "master" - digest = "1:67ee3460c086005f76f49c4d5f38a80ce11d68986de22979c75bf04e8372db9c" + digest = "1:e1fab80fa3cdc778a1d71494b403f287c50d810e9e65455baf5bd6982c5494c4" name = "golang.org/x/net" packages = [ "bpf", @@ -601,18 +601,18 @@ "ipv6", ] pruneopts = "UT" - revision = "b7e296877c6e5e01665044d943c8e6d10ce72a99" + revision = "adae6a3d119ae4890b46832a2e88a95adc62b8e7" [[projects]] branch = "master" - digest = "1:417d27a82efb8473554234a282be33d23b0d6adc121e636b55950f913ac071d6" + digest = "1:a089387bbfcfa884e5aafeebb85929993195bb8959766b1d20102841d47e0b0e" name = "golang.org/x/sys" packages = [ "unix", "windows", ] pruneopts = "UT" - revision = "9b800f95dbbc54abff0acf7ee32d88ba4e328c89" + revision = "93218def8b18e66adbdab3eca8ec334700329f1f" [[projects]] digest = "1:342378ac4dcb378a5448dd723f0784ae519383532f5e70ade24132c4c8693202" diff --git a/vendor/github.com/alecthomas/chroma/README.md b/vendor/github.com/alecthomas/chroma/README.md index 681f10ab8..b33272155 100644 --- a/vendor/github.com/alecthomas/chroma/README.md +++ b/vendor/github.com/alecthomas/chroma/README.md @@ -29,7 +29,32 @@ translators for Pygments lexers and styles. ## Supported languages -ABNF, ANTLR, APL, ActionScript, ActionScript 3, Ada, Angular2, ApacheConf, AppleScript, Awk, BNF, Ballerina, Base Makefile, Bash, Batchfile, BlitzBasic, Brainfuck, C, C#, C++, CFEngine3, CMake, COBOL, CSS, Cap'n Proto, Ceylon, ChaiScript, Cheetah, Clojure, CoffeeScript, Common Lisp, Coq, Crystal, Cython, DTD, Dart, Diff, Django/Jinja, Docker, EBNF, Elixir, Elm, EmacsLisp, Erlang, FSharp, Factor, Fish, Forth, Fortran, GAS, GDScript, GLSL, Genshi, Genshi HTML, Genshi Text, Gnuplot, Go, Go HTML Template, Go Text Template, Groovy, HTML, HTTP, Handlebars, Haskell, Haxe, Hexdump, Hy, INI, Idris, Io, JSON, JSX, Java, JavaScript, Julia, Kotlin, LLVM, Lighttpd configuration file, Lua, Mako, Mason, Mathematica, MiniZinc, Modula-2, MorrowindScript, MySQL, Myghty, NASM, Newspeak, Nginx configuration file, Nim, Nix, OCaml, Objective-C, Octave, Org Mode, PHP, PL/pgSQL, POVRay, PacmanConf, Perl, Pig, PkgConfig, PostScript, PostgreSQL SQL dialect, PowerShell, Prolog, Protocol Buffer, Puppet, Python, Python 3, QBasic, R, Racket, Ragel, Rexx, Ruby, Rust, SCSS, SPARQL, SQL, Sass, Scala, Scheme, Scilab, Smalltalk, Smarty, Snobol, Solidity, SquidConf, Swift, TASM, TOML, Tcl, Tcsh, TeX, Termcap, Terminfo, Terraform, Thrift, Transact-SQL, Turtle, Twig, TypeScript, TypoScript, TypoScriptCssData, TypoScriptHtmlData, VHDL, VimL, WDTE, XML, Xorg, YAML, cfstatement, markdown, reStructuredText, reg, systemverilog, verilog +Prefix | Language +:----: | -------- +A | ABNF, ActionScript, ActionScript 3, Ada, Angular2, ANTLR, ApacheConf, APL, AppleScript, Awk +B | Ballerina, Base Makefile, Bash, Batchfile, BlitzBasic, BNF, Brainfuck +C | C, C#, C++, Cassandra CQL, CFEngine3, cfstatement/ColdFusion, CMake, COBOL, CSS, Cap'n Proto, Ceylon, ChaiScript, Cheetah, Clojure, CoffeeScript, Common Lisp, Coq, Crystal, Cython +D | Dart, Diff, Django/Jinja, Docker, DTD +E | EBNF, Elixir, Elm, EmacsLisp, Erlang +F | Factor, Fish, Forth, Fortran, FSharp +G | GAS, GDScript, GLSL, Genshi, Genshi HTML, Genshi Text, Gnuplot, Go, Go HTML Template, Go Text Template, Groovy +H | Handlebars, Haskell, Haxe, Hexdump, HTML, HTTP, Hy +I | Idris, INI, Io +J | Java, JavaScript, JSON, Jsx, Julia, Jungle +K | Kotlin +L | Lighttpd configuration file, LLVM, Lua +M | Mako, Markdown, Mason, Mathematica, MiniZinc, Modula-2, MonkeyC, MorrowindScript, Myghty, MySQL +N | NASM, Newspeak, Nginx configuration file, Nim, Nix +O | Objective-C, OCaml, Octave, OpenSCAD, Org Mode +P | PacmanConf, Perl, PHP, Pig, PkgConfig, Plaintext, PL/pgSQL, PostgreSQL SQL dialect, PostScript, POVRay, PowerShell, Prolog, Protocol Buffer, Puppet, Python, Python 3 +Q | QBasic +R | R, Racket, Ragel, reg, reStructuredText, Rexx, Ruby, Rust +S | Sass, Scala, Scheme, Scilab, SCSS, Smalltalk, Smarty, Snobol, Solidity, SPARQL, SQL, SquidConf, Swift, systemd, Systemverilog +T | TASM, Tcl, Tcsh, Termcap, Terminfo, Terraform, TeX, Thrift, TOML, TradingView, Transact-SQL, Turtle, Twig, TypeScript, TypoScript, TypoScriptCssData, TypoScriptHtmlData +V | verilog, VHDL, VimL +W | WDTE +X | XML, Xorg +Y | YAML _I will attempt to keep this section up to date, but an authoritative list can be displayed with `chroma --list`._ diff --git a/vendor/github.com/alecthomas/chroma/formatters/html/html.go b/vendor/github.com/alecthomas/chroma/formatters/html/html.go index 806721cca..05e464fa7 100644 --- a/vendor/github.com/alecthomas/chroma/formatters/html/html.go +++ b/vendor/github.com/alecthomas/chroma/formatters/html/html.go @@ -25,6 +25,9 @@ func WithClasses() Option { return func(f *Formatter) { f.Classes = true } } // TabWidth sets the number of characters for a tab. Defaults to 8. func TabWidth(width int) Option { return func(f *Formatter) { f.tabWidth = width } } +// PreventSurroundingPre prevents the surrounding pre tags around the generated code +func PreventSurroundingPre() Option { return func(f *Formatter) { f.preventSurroundingPre = true } } + // WithLineNumbers formats output with line numbers. func WithLineNumbers() Option { return func(f *Formatter) { @@ -70,14 +73,15 @@ func New(options ...Option) *Formatter { // Formatter that generates HTML. type Formatter struct { - standalone bool - prefix string - Classes bool // Exported field to detect when classes are being used - tabWidth int - lineNumbers bool - lineNumbersInTable bool - highlightRanges highlightRanges - baseLineNumber int + standalone bool + prefix string + Classes bool // Exported field to detect when classes are being used + preventSurroundingPre bool + tabWidth int + lineNumbers bool + lineNumbersInTable bool + highlightRanges highlightRanges + baseLineNumber int } type highlightRanges [][2]int @@ -158,7 +162,9 @@ func (f *Formatter) writeHTML(w io.Writer, style *chroma.Style, tokens []*chroma fmt.Fprintf(w, "\n", f.styleAttr(css, chroma.Background)) fmt.Fprintf(w, "", f.styleAttr(css, chroma.LineTable)) fmt.Fprintf(w, "\n", f.styleAttr(css, chroma.LineTableTD)) - fmt.Fprintf(w, "", f.styleAttr(css, chroma.Background)) + if !f.preventSurroundingPre { + fmt.Fprintf(w, "", f.styleAttr(css, chroma.Background)) + } for index := range lines { line := f.baseLineNumber + index highlight, next := f.shouldHighlight(highlightIndex, line) @@ -175,11 +181,16 @@ func (f *Formatter) writeHTML(w io.Writer, style *chroma.Style, tokens []*chroma fmt.Fprintf(w, "") } } - fmt.Fprint(w, "\n") + if !f.preventSurroundingPre { + fmt.Fprint(w, "") + } + fmt.Fprint(w, "\n") fmt.Fprintf(w, "\n", f.styleAttr(css, chroma.LineTableTD)) } - fmt.Fprintf(w, "", f.styleAttr(css, chroma.Background)) + if !f.preventSurroundingPre { + fmt.Fprintf(w, "", f.styleAttr(css, chroma.Background)) + } highlightIndex = 0 for index, tokens := range lines { // 1-based line number. @@ -209,7 +220,9 @@ func (f *Formatter) writeHTML(w io.Writer, style *chroma.Style, tokens []*chroma } } - fmt.Fprint(w, "") + if !f.preventSurroundingPre { + fmt.Fprint(w, "") + } if wrapInTable { fmt.Fprint(w, "\n") diff --git a/vendor/github.com/alecthomas/chroma/lexers/b/bash.go b/vendor/github.com/alecthomas/chroma/lexers/b/bash.go index ff4e4a809..131e432f7 100644 --- a/vendor/github.com/alecthomas/chroma/lexers/b/bash.go +++ b/vendor/github.com/alecthomas/chroma/lexers/b/bash.go @@ -36,7 +36,7 @@ var Bash = internal.Register(MustNewLexer( {`\b(if|fi|else|while|do|done|for|then|return|function|case|select|continue|until|esac|elif)(\s*)\b`, ByGroups(Keyword, Text), nil}, {"\\b(alias|bg|bind|break|builtin|caller|cd|command|compgen|complete|declare|dirs|disown|echo|enable|eval|exec|exit|export|false|fc|fg|getopts|hash|help|history|jobs|kill|let|local|logout|popd|printf|pushd|pwd|read|readonly|set|shift|shopt|source|suspend|test|time|times|trap|true|type|typeset|ulimit|umask|unalias|unset|wait)(?=[\\s)`])", NameBuiltin, nil}, {`\A#!.+\n`, CommentPreproc, nil}, - {`#.*\n`, CommentSingle, nil}, + {`#.*\S`, CommentSingle, nil}, {`\\[\w\W]`, LiteralStringEscape, nil}, {`(\b\w+)(\s*)(\+?=)`, ByGroups(NameVariable, Text, Operator), nil}, {`[\[\]{}()=]`, Operator, nil}, diff --git a/vendor/github.com/alecthomas/chroma/lexers/e/elixir.go b/vendor/github.com/alecthomas/chroma/lexers/e/elixir.go index 23fa8af6c..40730290b 100644 --- a/vendor/github.com/alecthomas/chroma/lexers/e/elixir.go +++ b/vendor/github.com/alecthomas/chroma/lexers/e/elixir.go @@ -36,9 +36,9 @@ var Elixir = internal.Register(MustNewLexer( {`\\\\|\<\<|\>\>|\=\>|\(|\)|\:|\;|\,|\[|\]`, Punctuation, nil}, {`&\d`, NameEntity, nil}, {`\<|\>|\+|\-|\*|\/|\!|\^|\&`, Operator, nil}, - {`0b[01]+`, LiteralNumberBin, nil}, - {`0o[0-7]+`, LiteralNumberOct, nil}, - {`0x[\da-fA-F]+`, LiteralNumberHex, nil}, + {`0b[01](_?[01])*`, LiteralNumberBin, nil}, + {`0o[0-7](_?[0-7])*`, LiteralNumberOct, nil}, + {`0x[\da-fA-F](_?[\dA-Fa-f])*`, LiteralNumberHex, nil}, {`\d(_?\d)*\.\d(_?\d)*([eE][-+]?\d(_?\d)*)?`, LiteralNumberFloat, nil}, {`\d(_?\d)*`, LiteralNumberInteger, nil}, {`"""\s*`, LiteralStringHeredoc, Push("heredoc_double")}, diff --git a/vendor/github.com/alecthomas/chroma/lexers/g/go.go b/vendor/github.com/alecthomas/chroma/lexers/g/go.go index 54c23c3cf..a93fa8bbb 100644 --- a/vendor/github.com/alecthomas/chroma/lexers/g/go.go +++ b/vendor/github.com/alecthomas/chroma/lexers/g/go.go @@ -42,6 +42,7 @@ var Go = internal.Register(MustNewLexer( {"(`)([^`]*)(`)", ByGroups(LiteralString, Using(TypeRemappingLexer(GoTextTemplate, TypeMapping{{Other, LiteralString, nil}})), LiteralString), nil}, {`"(\\\\|\\"|[^"])*"`, LiteralString, nil}, {`(<<=|>>=|<<|>>|<=|>=|&\^=|&\^|\+=|-=|\*=|/=|%=|&=|\|=|&&|\|\||<-|\+\+|--|==|!=|:=|\.\.\.|[+\-*/%&])`, Operator, nil}, + {`([a-zA-Z_]\w*)(\s*)(\()`, ByGroups(NameFunction, UsingSelf("root"), Punctuation), nil}, {`[|^<>=!()\[\]{}.,;:]`, Punctuation, nil}, {`[^\W\d]\w*`, NameOther, nil}, }, diff --git a/vendor/github.com/alecthomas/chroma/lexers/h/http.go b/vendor/github.com/alecthomas/chroma/lexers/h/http.go index 67bc71a45..b8c297291 100644 --- a/vendor/github.com/alecthomas/chroma/lexers/h/http.go +++ b/vendor/github.com/alecthomas/chroma/lexers/h/http.go @@ -85,6 +85,7 @@ func (d *httpBodyContentTyper) Tokenise(options *TokeniseOptions, text string) ( } case token.Type == Literal && isContentType: { + isContentType = false contentType = strings.TrimSpace(token.Value) pos := strings.Index(contentType, ";") if pos > 0 { diff --git a/vendor/github.com/alecthomas/chroma/lexers/s/systemd.go b/vendor/github.com/alecthomas/chroma/lexers/s/systemd.go new file mode 100644 index 000000000..e2f7e9b7d --- /dev/null +++ b/vendor/github.com/alecthomas/chroma/lexers/s/systemd.go @@ -0,0 +1,29 @@ +package s + +import ( + . "github.com/alecthomas/chroma" // nolint + "github.com/alecthomas/chroma/lexers/internal" +) + +var SYSTEMD = internal.Register(MustNewLexer( + &Config{ + Name: "SYSTEMD", + Aliases: []string{"systemd"}, + Filenames: []string{"*.service"}, + MimeTypes: []string{"text/plain"}, + }, + Rules { + "root": { + {`\s+`, Text, nil}, + {`[;#].*`, Comment, nil}, + {`\[.*?\]$`, Keyword, nil}, + {`(.*?)(=)(.*)(\\\n)`, ByGroups(NameAttribute, Operator, LiteralString, Text), Push("continuation")}, + {`(.*?)(=)(.*)`, ByGroups(NameAttribute, Operator, LiteralString), nil}, + }, + "continuation": { + {`(.*?)(\\\n)`, ByGroups(LiteralString, Text), nil}, + {`(.*)`, LiteralString, Pop(1)}, + }, + }, +)) + diff --git a/vendor/github.com/alecthomas/chroma/lexers/t/tradingview.go b/vendor/github.com/alecthomas/chroma/lexers/t/tradingview.go new file mode 100644 index 000000000..e411411e3 --- /dev/null +++ b/vendor/github.com/alecthomas/chroma/lexers/t/tradingview.go @@ -0,0 +1,36 @@ +package t + +import ( + . "github.com/alecthomas/chroma" // nolint + "github.com/alecthomas/chroma/lexers/internal" +) + +// TradingView lexer. +var TradingView = internal.Register(MustNewLexer( + &Config{ + Name: "TradingView", + Aliases: []string{"tradingview", "tv"}, + Filenames: []string{"*.tv"}, + MimeTypes: []string{"text/x-tradingview"}, + DotAll: true, + }, + Rules{ + "root": { + {`[^\S\n]+|\n|[()]`, Text, nil}, + {`(//.*?)(\n)`, ByGroups(CommentSingle, Text), nil}, + {`>=|<=|==|!=|>|<|\?|-|\+|\*|\/|%|\[|\]`, Operator, nil}, + {`[:,.]`, Punctuation, nil}, + {`=`, KeywordPseudo, nil}, + {`"(\\\\|\\"|[^"\n])*["\n]`, LiteralString, nil}, + {`'\\.'|'[^\\]'`, LiteralString, nil}, + {`[0-9](\.[0-9]*)?([eE][+-][0-9]+)?`, LiteralNumber, nil}, + {`(abs|acos|alertcondition|alma|asin|atan|atr|avg|barcolor|barssince|bgcolor|cci|ceil|change|cog|correlation|cos|crossover|crossunder|cum|dev|ema|exp|falling|fill|fixnan|floor|heikinashi|highest|highestbars|hline|iff|input|kagi|linebreak|linreg|log|log10|lowest|lowestbars|macd|max|min|mom|nz|percentile_linear_interpolation|percentile_nearest_rank|percentrank|pivothigh|pivotlow|plot|plotarrow|plotbar|plotcandle|plotchar|plotshape|pointfigure|pow|renko|rising|rma|roc|round|rsi|sar|security|sign|sin|sma|sqrt|stdev|stoch|study|sum|swma|tan|tostring|tsi|valuewhen|variance|vwma|wma|strategy\.(cancel|cancel_all|close|close_all|entry|exit|order)|strategy\.risk\.(allow_entry_in|max_cons_loss_days|max_drawdown|max_intraday_filled_orders|max_intraday_loss|max_position_size))\b`, NameFunction, nil}, + {`\b(cross|dayofmonth|dayofweek|hour|minute|month|na|offset|second|tickerid|time|tr|vwap|weekofyear|year)(\()`, ByGroups(NameFunction, Text), nil}, // functions that can also be variable + {`(accdist|aqua|area|areabr|black|blue|bool|circles|close|columns|currency\.(AUD|CAD|CHF|EUR|GBP|HKD|JPY|NOK|NONE|NZD|SEK|SGD|TRY|USD|ZAR)|dashed|dotted|float|friday|fuchsia|gray|green|high|histogram|hl2|hlc3|integer|interval|isdaily|isdwm|isintraday|ismonthly|isweekly|lime|line|linebr|location\.(abovebar|belowbar|bottom|top)|low|maroon|monday|n|navy|ohlc4|olive|open|orange|period|purple|red|resolution|saturday|scale\.(left|none|right)|session|session\.(extended|regular)|silver|size\.(auto|huge|large|normal|small|tiny)|solid|source|string|sunday|symbol|syminfo\.(mintick|pointvalue|prefix|root|session)|teal|thursday|ticker|tuesday|volume|wednesday|white|yellow|strategy\.(cash|position_size|closedtrades|direction\.(all|long|short)|equity|eventrades|fixed|grossloss|grossprofit|initial_capital|long|losstrades|max_contracts_held_all|max_contracts_held_long|max_contracts_held_short|max_drawdown|netprofit|oca\.(cancel|none|reduce)|openprofit|opentrades|percent_of_equity|position_avg_price|position_entry_name|short|wintrades)|shape\.(arrowdown|arrowup|circle|cross|diamond|flag|labeldown|labelup|square|triangledown|triangleup|xcross)|barstate\.is(first|history|last|new|realtime)|barmerge\.(gaps_on|gaps_off|lookahead_on|lookahead_off)|strategy\.commission\.(cash_per_contract|cash_per_order|percent))\b`, NameVariable, nil}, + {`(cross|dayofmonth|dayofweek|hour|minute|month|na|second|tickerid|time|tr|vwap|weekofyear|year)(\b[^\(])`, ByGroups(NameVariable, Text), nil}, // variables that can also be function + {`(true|false)\b`, KeywordConstant, nil}, + {`(and|or|not|if|else|for|to)\b`, OperatorWord, nil}, + {`@?[_a-zA-Z]\w*`, Text, nil}, + }, + }, +)) diff --git a/vendor/github.com/fortytw2/leaktest/README.md b/vendor/github.com/fortytw2/leaktest/README.md index 2ce6c52c5..82822bf0a 100644 --- a/vendor/github.com/fortytw2/leaktest/README.md +++ b/vendor/github.com/fortytw2/leaktest/README.md @@ -1,11 +1,10 @@ -Leaktest [![Build Status](https://travis-ci.org/fortytw2/leaktest.svg?branch=master)](https://travis-ci.org/fortytw2/leaktest) [![codecov](https://codecov.io/gh/fortytw2/leaktest/branch/master/graph/badge.svg)](https://codecov.io/gh/fortytw2/leaktest) [![Sourcegraph](https://sourcegraph.com/github.com/fortytw2/leaktest/-/badge.svg)](https://sourcegraph.com/github.com/fortytw2/leaktest?badge) ------- +## Leaktest [![Build Status](https://travis-ci.org/fortytw2/leaktest.svg?branch=master)](https://travis-ci.org/fortytw2/leaktest) [![codecov](https://codecov.io/gh/fortytw2/leaktest/branch/master/graph/badge.svg)](https://codecov.io/gh/fortytw2/leaktest) [![Sourcegraph](https://sourcegraph.com/github.com/fortytw2/leaktest/-/badge.svg)](https://sourcegraph.com/github.com/fortytw2/leaktest?badge) [![Documentation](https://godoc.org/github.com/fortytw2/gpt?status.svg)](http://godoc.org/github.com/fortytw2/leaktest) -Refactored, tested variant of the goroutine leak detector found in both +Refactored, tested variant of the goroutine leak detector found in both `net/http` tests and the `cockroachdb` source tree. Takes a snapshot of running goroutines at the start of a test, and at the end - -compares the two and *voila*. Ignores runtime/sys goroutines. Doesn't play nice +compares the two and _voila_. Ignores runtime/sys goroutines. Doesn't play nice with `t.Parallel()` right now, but there are plans to do so. ### Installation @@ -17,7 +16,7 @@ go get -u github.com/fortytw2/leaktest ``` Go 1.5/1.6 need to use the tag `v1.0.0`, as newer versions depend on -`context.Context`. +`context.Context`. ### Example @@ -27,7 +26,7 @@ These tests fail, because they leak a goroutine // Default "Check" will poll for 5 seconds to check that all // goroutines are cleaned up func TestPool(t *testing.T) { - defer leaktest.Check(t)() + defer leaktest.Check(t)() go func() { for { @@ -38,7 +37,7 @@ func TestPool(t *testing.T) { // Helper function to timeout after X duration func TestPoolTimeout(t *testing.T) { - defer leaktest.CheckTimeout(t, time.Second)() + defer leaktest.CheckTimeout(t, time.Second)() go func() { for { @@ -50,7 +49,7 @@ func TestPoolTimeout(t *testing.T) { // Use Go 1.7+ context.Context for cancellation func TestPoolContext(t *testing.T) { ctx, _ := context.WithTimeout(context.Background(), time.Second) - defer leaktest.CheckContext(ctx, t)() + defer leaktest.CheckContext(ctx, t)() go func() { for { @@ -60,7 +59,6 @@ func TestPoolContext(t *testing.T) { } ``` +## LICENSE -LICENSE ------- Same BSD-style as Go, see LICENSE diff --git a/vendor/github.com/fortytw2/leaktest/leaktest.go b/vendor/github.com/fortytw2/leaktest/leaktest.go index f2dd985eb..219e9307d 100644 --- a/vendor/github.com/fortytw2/leaktest/leaktest.go +++ b/vendor/github.com/fortytw2/leaktest/leaktest.go @@ -3,7 +3,7 @@ // license that can be found in the LICENSE file. // Package leaktest provides tools to detect leaked goroutines in tests. -// To use it, call "defer util.Check(t)()" at the beginning of each +// To use it, call "defer leaktest.Check(t)()" at the beginning of each // test that may use goroutines. // copied out of the cockroachdb source tree with slight modifications to be // more re-useable @@ -41,6 +41,9 @@ func interestingGoroutine(g string) (*goroutine, error) { } if stack == "" || + // Ignore HTTP keep alives + strings.Contains(stack, ").readLoop(") || + strings.Contains(stack, ").writeLoop(") || // Below are the stacks ignored by the upstream leaktest code. strings.Contains(stack, "testing.Main(") || strings.Contains(stack, "testing.(*T).Run(") || diff --git a/vendor/github.com/jtolds/gls/stack_tags.go b/vendor/github.com/jtolds/gls/stack_tags.go index cc95dcd8e..37bbd3347 100644 --- a/vendor/github.com/jtolds/gls/stack_tags.go +++ b/vendor/github.com/jtolds/gls/stack_tags.go @@ -51,22 +51,56 @@ func addStackTag(tag uint, context_call func()) { // these private methods are named this horrendous name so gopherjs support // is easier. it shouldn't add any runtime cost in non-js builds. + +//go:noinline func github_com_jtolds_gls_markS(tag uint, cb func()) { _m(tag, cb) } + +//go:noinline func github_com_jtolds_gls_mark0(tag uint, cb func()) { _m(tag, cb) } + +//go:noinline func github_com_jtolds_gls_mark1(tag uint, cb func()) { _m(tag, cb) } + +//go:noinline func github_com_jtolds_gls_mark2(tag uint, cb func()) { _m(tag, cb) } + +//go:noinline func github_com_jtolds_gls_mark3(tag uint, cb func()) { _m(tag, cb) } + +//go:noinline func github_com_jtolds_gls_mark4(tag uint, cb func()) { _m(tag, cb) } + +//go:noinline func github_com_jtolds_gls_mark5(tag uint, cb func()) { _m(tag, cb) } + +//go:noinline func github_com_jtolds_gls_mark6(tag uint, cb func()) { _m(tag, cb) } + +//go:noinline func github_com_jtolds_gls_mark7(tag uint, cb func()) { _m(tag, cb) } + +//go:noinline func github_com_jtolds_gls_mark8(tag uint, cb func()) { _m(tag, cb) } + +//go:noinline func github_com_jtolds_gls_mark9(tag uint, cb func()) { _m(tag, cb) } + +//go:noinline func github_com_jtolds_gls_markA(tag uint, cb func()) { _m(tag, cb) } + +//go:noinline func github_com_jtolds_gls_markB(tag uint, cb func()) { _m(tag, cb) } + +//go:noinline func github_com_jtolds_gls_markC(tag uint, cb func()) { _m(tag, cb) } + +//go:noinline func github_com_jtolds_gls_markD(tag uint, cb func()) { _m(tag, cb) } + +//go:noinline func github_com_jtolds_gls_markE(tag uint, cb func()) { _m(tag, cb) } + +//go:noinline func github_com_jtolds_gls_markF(tag uint, cb func()) { _m(tag, cb) } func _m(tag_remainder uint, cb func()) { diff --git a/vendor/github.com/juju/errors/error.go b/vendor/github.com/juju/errors/error.go index d67e33863..169e3a4e3 100644 --- a/vendor/github.com/juju/errors/error.go +++ b/vendor/github.com/juju/errors/error.go @@ -143,6 +143,10 @@ func (e *Err) Format(s fmt.State, verb rune) { fallthrough case 's': fmt.Fprintf(s, "%s", e.Error()) + case 'q': + fmt.Fprintf(s, "%q", e.Error()) + default: + fmt.Fprintf(s, "%%!%c(%T=%s)", verb, e, e.Error()) } } diff --git a/vendor/github.com/miekg/dns/client.go b/vendor/github.com/miekg/dns/client.go index 63ced2bd0..770a946cd 100644 --- a/vendor/github.com/miekg/dns/client.go +++ b/vendor/github.com/miekg/dns/client.go @@ -7,11 +7,8 @@ import ( "context" "crypto/tls" "encoding/binary" - "fmt" "io" - "io/ioutil" "net" - "net/http" "strings" "time" ) @@ -19,8 +16,6 @@ import ( const ( dnsTimeout time.Duration = 2 * time.Second tcpIdleTimeout time.Duration = 8 * time.Second - - dohMimeType = "application/dns-message" ) // A Conn represents a connection to a DNS server. @@ -44,7 +39,6 @@ type Client struct { DialTimeout time.Duration // net.DialTimeout, defaults to 2 seconds, or net.Dialer.Timeout if expiring earlier - overridden by Timeout when that value is non-zero ReadTimeout time.Duration // net.Conn.SetReadTimeout value for connections, defaults to 2 seconds - overridden by Timeout when that value is non-zero WriteTimeout time.Duration // net.Conn.SetWriteTimeout value for connections, defaults to 2 seconds - overridden by Timeout when that value is non-zero - HTTPClient *http.Client // The http.Client to use for DNS-over-HTTPS TsigSecret map[string]string // secret(s) for Tsig map[], zonename must be in canonical form (lowercase, fqdn, see RFC 4034 Section 6.2) SingleInflight bool // if true suppress multiple outstanding queries for the same Qname, Qtype and Qclass group singleflight @@ -132,11 +126,6 @@ func (c *Client) Dial(address string) (conn *Conn, err error) { // attribute appropriately func (c *Client) Exchange(m *Msg, address string) (r *Msg, rtt time.Duration, err error) { if !c.SingleInflight { - if c.Net == "https" { - // TODO(tmthrgd): pipe timeouts into exchangeDOH - return c.exchangeDOH(context.TODO(), m, address) - } - return c.exchange(m, address) } @@ -149,11 +138,6 @@ func (c *Client) Exchange(m *Msg, address string) (r *Msg, rtt time.Duration, er cl = cl1 } r, rtt, err, shared := c.group.Do(m.Question[0].Name+t+cl, func() (*Msg, time.Duration, error) { - if c.Net == "https" { - // TODO(tmthrgd): pipe timeouts into exchangeDOH - return c.exchangeDOH(context.TODO(), m, address) - } - return c.exchange(m, address) }) if r != nil && shared { @@ -199,67 +183,6 @@ func (c *Client) exchange(m *Msg, a string) (r *Msg, rtt time.Duration, err erro return r, rtt, err } -func (c *Client) exchangeDOH(ctx context.Context, m *Msg, a string) (r *Msg, rtt time.Duration, err error) { - p, err := m.Pack() - if err != nil { - return nil, 0, err - } - - req, err := http.NewRequest(http.MethodPost, a, bytes.NewReader(p)) - if err != nil { - return nil, 0, err - } - - req.Header.Set("Content-Type", dohMimeType) - req.Header.Set("Accept", dohMimeType) - - hc := http.DefaultClient - if c.HTTPClient != nil { - hc = c.HTTPClient - } - - if ctx != context.Background() && ctx != context.TODO() { - req = req.WithContext(ctx) - } - - t := time.Now() - - resp, err := hc.Do(req) - if err != nil { - return nil, 0, err - } - defer closeHTTPBody(resp.Body) - - if resp.StatusCode != http.StatusOK { - return nil, 0, fmt.Errorf("dns: server returned HTTP %d error: %q", resp.StatusCode, resp.Status) - } - - if ct := resp.Header.Get("Content-Type"); ct != dohMimeType { - return nil, 0, fmt.Errorf("dns: unexpected Content-Type %q; expected %q", ct, dohMimeType) - } - - p, err = ioutil.ReadAll(resp.Body) - if err != nil { - return nil, 0, err - } - - rtt = time.Since(t) - - r = new(Msg) - if err := r.Unpack(p); err != nil { - return r, 0, err - } - - // TODO: TSIG? Is it even supported over DoH? - - return r, rtt, nil -} - -func closeHTTPBody(r io.ReadCloser) error { - io.Copy(ioutil.Discard, io.LimitReader(r, 8<<20)) - return r.Close() -} - // ReadMsg reads a message from the connection co. // If the received message contains a TSIG record the transaction signature // is verified. This method always tries to return the message, however if an @@ -559,10 +482,6 @@ func DialTimeoutWithTLS(network, address string, tlsConfig *tls.Config, timeout // context, if present. If there is both a context deadline and a configured // timeout on the client, the earliest of the two takes effect. func (c *Client) ExchangeContext(ctx context.Context, m *Msg, a string) (r *Msg, rtt time.Duration, err error) { - if !c.SingleInflight && c.Net == "https" { - return c.exchangeDOH(ctx, m, a) - } - var timeout time.Duration if deadline, ok := ctx.Deadline(); !ok { timeout = 0 @@ -571,7 +490,7 @@ func (c *Client) ExchangeContext(ctx context.Context, m *Msg, a string) (r *Msg, } // not passing the context to the underlying calls, as the API does not support // context. For timeouts you should set up Client.Dialer and call Client.Exchange. - // TODO(tmthrgd): this is a race condition + // TODO(tmthrgd,miekg): this is a race condition. c.Dialer = &net.Dialer{Timeout: timeout} return c.Exchange(m, a) } diff --git a/vendor/github.com/miekg/dns/server.go b/vendor/github.com/miekg/dns/server.go index 4b4ec33c8..06984e7ca 100644 --- a/vendor/github.com/miekg/dns/server.go +++ b/vendor/github.com/miekg/dns/server.go @@ -82,6 +82,7 @@ type ConnectionStater interface { type response struct { msg []byte + closed bool // connection has been closed hijacked bool // connection has been hijacked by handler tsigTimersOnly bool tsigStatus error @@ -728,6 +729,10 @@ func (srv *Server) readUDP(conn *net.UDPConn, timeout time.Duration) ([]byte, *S // WriteMsg implements the ResponseWriter.WriteMsg method. func (w *response) WriteMsg(m *Msg) (err error) { + if w.closed { + return &Error{err: "WriteMsg called after Close"} + } + var data []byte if w.tsigSecret != nil { // if no secrets, dont check for the tsig (which is a longer check) if t := m.IsTsig(); t != nil { @@ -749,6 +754,10 @@ func (w *response) WriteMsg(m *Msg) (err error) { // Write implements the ResponseWriter.Write method. func (w *response) Write(m []byte) (int, error) { + if w.closed { + return 0, &Error{err: "Write called after Close"} + } + switch { case w.udp != nil: n, err := WriteToSessionUDP(w.udp, m, w.udpSession) @@ -768,7 +777,7 @@ func (w *response) Write(m []byte) (int, error) { n, err := io.Copy(w.tcp, bytes.NewReader(m)) return int(n), err default: - panic("dns: Write called after Close") + panic("dns: internal error: udp and tcp both nil") } } @@ -780,7 +789,7 @@ func (w *response) LocalAddr() net.Addr { case w.tcp != nil: return w.tcp.LocalAddr() default: - panic("dns: LocalAddr called after Close") + panic("dns: internal error: udp and tcp both nil") } } @@ -792,7 +801,7 @@ func (w *response) RemoteAddr() net.Addr { case w.tcp != nil: return w.tcp.RemoteAddr() default: - panic("dns: RemoteAddr called after Close") + panic("dns: internal error: udpSession and tcp both nil") } } @@ -807,13 +816,20 @@ func (w *response) Hijack() { w.hijacked = true } // Close implements the ResponseWriter.Close method func (w *response) Close() error { - // Can't close the udp conn, as that is actually the listener. - if w.tcp != nil { - e := w.tcp.Close() - w.tcp = nil - return e + if w.closed { + return &Error{err: "connection already closed"} + } + w.closed = true + + switch { + case w.udp != nil: + // Can't close the udp conn, as that is actually the listener. + return nil + case w.tcp != nil: + return w.tcp.Close() + default: + panic("dns: internal error: udp and tcp both nil") } - return nil } // ConnectionState() implements the ConnectionStater.ConnectionState() interface. diff --git a/vendor/github.com/miekg/dns/version.go b/vendor/github.com/miekg/dns/version.go index 0b0e9b6d8..e7c82e999 100644 --- a/vendor/github.com/miekg/dns/version.go +++ b/vendor/github.com/miekg/dns/version.go @@ -3,7 +3,7 @@ package dns import "fmt" // Version is current version of this library. -var Version = V{1, 0, 14} +var Version = V{1, 0, 15} // V holds the version of this library. type V struct { diff --git a/vendor/github.com/prometheus/client_golang/prometheus/counter.go b/vendor/github.com/prometheus/client_golang/prometheus/counter.go index 765e4550c..d463e36d3 100644 --- a/vendor/github.com/prometheus/client_golang/prometheus/counter.go +++ b/vendor/github.com/prometheus/client_golang/prometheus/counter.go @@ -136,7 +136,7 @@ func NewCounterVec(opts CounterOpts, labelNames []string) *CounterVec { return &CounterVec{ metricVec: newMetricVec(desc, func(lvs ...string) Metric { if len(lvs) != len(desc.variableLabels) { - panic(errInconsistentCardinality) + panic(makeInconsistentCardinalityError(desc.fqName, desc.variableLabels, lvs)) } result := &counter{desc: desc, labelPairs: makeLabelPairs(desc, lvs)} result.init(result) // Init self-collection. diff --git a/vendor/github.com/prometheus/client_golang/prometheus/gauge.go b/vendor/github.com/prometheus/client_golang/prometheus/gauge.go index 17c72d7eb..71d406bd9 100644 --- a/vendor/github.com/prometheus/client_golang/prometheus/gauge.go +++ b/vendor/github.com/prometheus/client_golang/prometheus/gauge.go @@ -147,7 +147,7 @@ func NewGaugeVec(opts GaugeOpts, labelNames []string) *GaugeVec { return &GaugeVec{ metricVec: newMetricVec(desc, func(lvs ...string) Metric { if len(lvs) != len(desc.variableLabels) { - panic(errInconsistentCardinality) + panic(makeInconsistentCardinalityError(desc.fqName, desc.variableLabels, lvs)) } result := &gauge{desc: desc, labelPairs: makeLabelPairs(desc, lvs)} result.init(result) // Init self-collection. diff --git a/vendor/github.com/prometheus/client_golang/prometheus/histogram.go b/vendor/github.com/prometheus/client_golang/prometheus/histogram.go index 4d7fa976e..f88da707b 100644 --- a/vendor/github.com/prometheus/client_golang/prometheus/histogram.go +++ b/vendor/github.com/prometheus/client_golang/prometheus/histogram.go @@ -165,7 +165,7 @@ func NewHistogram(opts HistogramOpts) Histogram { func newHistogram(desc *Desc, opts HistogramOpts, labelValues ...string) Histogram { if len(desc.variableLabels) != len(labelValues) { - panic(errInconsistentCardinality) + panic(makeInconsistentCardinalityError(desc.fqName, desc.variableLabels, labelValues)) } for _, n := range desc.variableLabels { diff --git a/vendor/github.com/prometheus/client_golang/prometheus/labels.go b/vendor/github.com/prometheus/client_golang/prometheus/labels.go index e68f132ec..2744443ac 100644 --- a/vendor/github.com/prometheus/client_golang/prometheus/labels.go +++ b/vendor/github.com/prometheus/client_golang/prometheus/labels.go @@ -37,9 +37,22 @@ const reservedLabelPrefix = "__" var errInconsistentCardinality = errors.New("inconsistent label cardinality") +func makeInconsistentCardinalityError(fqName string, labels, labelValues []string) error { + return fmt.Errorf( + "%s: %q has %d variable labels named %q but %d values %q were provided", + errInconsistentCardinality, fqName, + len(labels), labels, + len(labelValues), labelValues, + ) +} + func validateValuesInLabels(labels Labels, expectedNumberOfValues int) error { if len(labels) != expectedNumberOfValues { - return errInconsistentCardinality + return fmt.Errorf( + "%s: expected %d label values but got %d in %#v", + errInconsistentCardinality, expectedNumberOfValues, + len(labels), labels, + ) } for name, val := range labels { @@ -53,7 +66,11 @@ func validateValuesInLabels(labels Labels, expectedNumberOfValues int) error { func validateLabelValues(vals []string, expectedNumberOfValues int) error { if len(vals) != expectedNumberOfValues { - return errInconsistentCardinality + return fmt.Errorf( + "%s: expected %d label values but got %d in %#v", + errInconsistentCardinality, expectedNumberOfValues, + len(vals), vals, + ) } for _, val := range vals { diff --git a/vendor/github.com/prometheus/client_golang/prometheus/registry.go b/vendor/github.com/prometheus/client_golang/prometheus/registry.go index e422ef383..f98c81a86 100644 --- a/vendor/github.com/prometheus/client_golang/prometheus/registry.go +++ b/vendor/github.com/prometheus/client_golang/prometheus/registry.go @@ -16,6 +16,9 @@ package prometheus import ( "bytes" "fmt" + "io/ioutil" + "os" + "path/filepath" "runtime" "sort" "strings" @@ -23,6 +26,7 @@ import ( "unicode/utf8" "github.com/golang/protobuf/proto" + "github.com/prometheus/common/expfmt" dto "github.com/prometheus/client_model/go" @@ -533,6 +537,38 @@ func (r *Registry) Gather() ([]*dto.MetricFamily, error) { return internal.NormalizeMetricFamilies(metricFamiliesByName), errs.MaybeUnwrap() } +// WriteToTextfile calls Gather on the provided Gatherer, encodes the result in the +// Prometheus text format, and writes it to a temporary file. Upon success, the +// temporary file is renamed to the provided filename. +// +// This is intended for use with the textfile collector of the node exporter. +// Note that the node exporter expects the filename to be suffixed with ".prom". +func WriteToTextfile(filename string, g Gatherer) error { + tmp, err := ioutil.TempFile(filepath.Dir(filename), filepath.Base(filename)) + if err != nil { + return err + } + defer os.Remove(tmp.Name()) + + mfs, err := g.Gather() + if err != nil { + return err + } + for _, mf := range mfs { + if _, err := expfmt.MetricFamilyToText(tmp, mf); err != nil { + return err + } + } + if err := tmp.Close(); err != nil { + return err + } + + if err := os.Chmod(tmp.Name(), 0644); err != nil { + return err + } + return os.Rename(tmp.Name(), filename) +} + // processMetric is an internal helper method only used by the Gather method. func processMetric( metric Metric, diff --git a/vendor/github.com/prometheus/client_golang/prometheus/summary.go b/vendor/github.com/prometheus/client_golang/prometheus/summary.go index f7e92d829..2980614df 100644 --- a/vendor/github.com/prometheus/client_golang/prometheus/summary.go +++ b/vendor/github.com/prometheus/client_golang/prometheus/summary.go @@ -181,7 +181,7 @@ func NewSummary(opts SummaryOpts) Summary { func newSummary(desc *Desc, opts SummaryOpts, labelValues ...string) Summary { if len(desc.variableLabels) != len(labelValues) { - panic(errInconsistentCardinality) + panic(makeInconsistentCardinalityError(desc.fqName, desc.variableLabels, labelValues)) } for _, n := range desc.variableLabels { diff --git a/vendor/github.com/siddontang/go-mysql/server/auth.go b/vendor/github.com/siddontang/go-mysql/server/auth.go index cb6ca160f..ac71ce723 100644 --- a/vendor/github.com/siddontang/go-mysql/server/auth.go +++ b/vendor/github.com/siddontang/go-mysql/server/auth.go @@ -58,7 +58,7 @@ func (c *Conn) writeInitialHandshake() error { return c.WritePacket(data) } -func (c *Conn) readHandshakeResponse(password string) error { +func (c *Conn) readHandshakeResponse() error { data, err := c.ReadPacket() if err != nil { @@ -85,10 +85,14 @@ func (c *Conn) readHandshakeResponse(password string) error { user := string(data[pos : pos+bytes.IndexByte(data[pos:], 0)]) pos += len(user) + 1 - if c.user != user { + var password string + var userExists bool + if password, userExists = c.users[user]; !userExists { return NewDefaultError(ER_NO_SUCH_USER, user, c.RemoteAddr().String()) } + c.user = user + var auth []byte if c.capability&CLIENT_PLUGIN_AUTH_LENENC_CLIENT_DATA > 0 { diff --git a/vendor/github.com/siddontang/go-mysql/server/conn.go b/vendor/github.com/siddontang/go-mysql/server/conn.go index d6ea846ae..ba48123fc 100644 --- a/vendor/github.com/siddontang/go-mysql/server/conn.go +++ b/vendor/github.com/siddontang/go-mysql/server/conn.go @@ -23,6 +23,8 @@ type Conn struct { user string + users map[string]string // user to password map + salt []byte h Handler @@ -36,11 +38,24 @@ type Conn struct { var baseConnID uint32 = 10000 func NewConn(conn net.Conn, user string, password string, h Handler) (*Conn, error) { + userPasswords := map[string]string{ + user: password, + } + + return NewConnWithUsers(conn, userPasswords, h) +} + +func NewConnWithUsers(conn net.Conn, userPasswords map[string]string, h Handler) (*Conn, error) { c := new(Conn) c.h = h - c.user = user + c.users = make(map[string]string) + + for u, p := range userPasswords { + c.users[u] = p + } + c.Conn = packet.NewConn(conn) c.connectionID = atomic.AddUint32(&baseConnID, 1) @@ -51,7 +66,7 @@ func NewConn(conn net.Conn, user string, password string, h Handler) (*Conn, err c.closed.Set(false) - if err := c.handshake(password); err != nil { + if err := c.handshake(); err != nil { c.Close() return nil, err } @@ -59,12 +74,12 @@ func NewConn(conn net.Conn, user string, password string, h Handler) (*Conn, err return c, nil } -func (c *Conn) handshake(password string) error { +func (c *Conn) handshake() error { if err := c.writeInitialHandshake(); err != nil { return err } - if err := c.readHandshakeResponse(password); err != nil { + if err := c.readHandshakeResponse(); err != nil { c.writeError(err) return err diff --git a/vendor/github.com/syndtr/goleveldb/leveldb/cache/cache.go b/vendor/github.com/syndtr/goleveldb/leveldb/cache/cache.go index c5940b232..c36ad3235 100644 --- a/vendor/github.com/syndtr/goleveldb/leveldb/cache/cache.go +++ b/vendor/github.com/syndtr/goleveldb/leveldb/cache/cache.go @@ -331,7 +331,6 @@ func (r *Cache) delete(n *Node) bool { return deleted } } - return false } // Nodes returns number of 'cache node' in the map. diff --git a/vendor/github.com/syndtr/goleveldb/leveldb/comparer/bytes_comparer.go b/vendor/github.com/syndtr/goleveldb/leveldb/comparer/bytes_comparer.go index 14dddf88d..abf9fb65c 100644 --- a/vendor/github.com/syndtr/goleveldb/leveldb/comparer/bytes_comparer.go +++ b/vendor/github.com/syndtr/goleveldb/leveldb/comparer/bytes_comparer.go @@ -29,7 +29,7 @@ func (bytesComparer) Separator(dst, a, b []byte) []byte { // Do not shorten if one string is a prefix of the other } else if c := a[i]; c < 0xff && c+1 < b[i] { dst = append(dst, a[:i+1]...) - dst[i]++ + dst[len(dst)-1]++ return dst } return nil @@ -39,7 +39,7 @@ func (bytesComparer) Successor(dst, b []byte) []byte { for i, c := range b { if c != 0xff { dst = append(dst, b[:i+1]...) - dst[i]++ + dst[len(dst)-1]++ return dst } } diff --git a/vendor/github.com/syndtr/goleveldb/leveldb/db.go b/vendor/github.com/syndtr/goleveldb/leveldb/db.go index e7ac06541..b27c38d37 100644 --- a/vendor/github.com/syndtr/goleveldb/leveldb/db.go +++ b/vendor/github.com/syndtr/goleveldb/leveldb/db.go @@ -182,7 +182,7 @@ func Open(stor storage.Storage, o *opt.Options) (db *DB, err error) { err = s.recover() if err != nil { - if !os.IsNotExist(err) || s.o.GetErrorIfMissing() { + if !os.IsNotExist(err) || s.o.GetErrorIfMissing() || s.o.GetReadOnly() { return } err = s.create() diff --git a/vendor/github.com/syndtr/goleveldb/leveldb/db_util.go b/vendor/github.com/syndtr/goleveldb/leveldb/db_util.go index 7ecd960d2..3f0654894 100644 --- a/vendor/github.com/syndtr/goleveldb/leveldb/db_util.go +++ b/vendor/github.com/syndtr/goleveldb/leveldb/db_util.go @@ -84,7 +84,7 @@ func (db *DB) checkAndCleanFiles() error { var mfds []storage.FileDesc for num, present := range tmap { if !present { - mfds = append(mfds, storage.FileDesc{storage.TypeTable, num}) + mfds = append(mfds, storage.FileDesc{Type: storage.TypeTable, Num: num}) db.logf("db@janitor table missing @%d", num) } } diff --git a/vendor/github.com/syndtr/goleveldb/leveldb/session_util.go b/vendor/github.com/syndtr/goleveldb/leveldb/session_util.go index 92328933c..40cb2cf95 100644 --- a/vendor/github.com/syndtr/goleveldb/leveldb/session_util.go +++ b/vendor/github.com/syndtr/goleveldb/leveldb/session_util.go @@ -36,7 +36,7 @@ func (s *session) logf(format string, v ...interface{}) { s.stor.Log(fmt.Sprintf func (s *session) newTemp() storage.FileDesc { num := atomic.AddInt64(&s.stTempFileNum, 1) - 1 - return storage.FileDesc{storage.TypeTemp, num} + return storage.FileDesc{Type: storage.TypeTemp, Num: num} } func (s *session) addFileRef(fd storage.FileDesc, ref int) int { @@ -190,7 +190,7 @@ func (s *session) recordCommited(rec *sessionRecord) { // Create a new manifest file; need external synchronization. func (s *session) newManifest(rec *sessionRecord, v *version) (err error) { - fd := storage.FileDesc{storage.TypeManifest, s.allocFileNum()} + fd := storage.FileDesc{Type: storage.TypeManifest, Num: s.allocFileNum()} writer, err := s.stor.Create(fd) if err != nil { return diff --git a/vendor/github.com/syndtr/goleveldb/leveldb/table.go b/vendor/github.com/syndtr/goleveldb/leveldb/table.go index adf773f13..87f5a9319 100644 --- a/vendor/github.com/syndtr/goleveldb/leveldb/table.go +++ b/vendor/github.com/syndtr/goleveldb/leveldb/table.go @@ -78,7 +78,7 @@ func newTableFile(fd storage.FileDesc, size int64, imin, imax internalKey) *tFil } func tableFileFromRecord(r atRecord) *tFile { - return newTableFile(storage.FileDesc{storage.TypeTable, r.num}, r.size, r.imin, r.imax) + return newTableFile(storage.FileDesc{Type: storage.TypeTable, Num: r.num}, r.size, r.imin, r.imax) } // tFiles hold multiple tFile. @@ -299,7 +299,7 @@ type tOps struct { // Creates an empty table and returns table writer. func (t *tOps) create() (*tWriter, error) { - fd := storage.FileDesc{storage.TypeTable, t.s.allocFileNum()} + fd := storage.FileDesc{Type: storage.TypeTable, Num: t.s.allocFileNum()} fw, err := t.s.stor.Create(fd) if err != nil { return nil, err diff --git a/vendor/github.com/xo/dburl/README.md b/vendor/github.com/xo/dburl/README.md index 9d30c5436..cb156c1d4 100644 --- a/vendor/github.com/xo/dburl/README.md +++ b/vendor/github.com/xo/dburl/README.md @@ -174,7 +174,7 @@ standard library's `net/url.Parse`. A full example for reference: ```go -// example/example.go +// _example/example.go package main import ( diff --git a/vendor/github.com/xo/dburl/dsn.go b/vendor/github.com/xo/dburl/dsn.go index e386f4bc3..6d462c64f 100644 --- a/vendor/github.com/xo/dburl/dsn.go +++ b/vendor/github.com/xo/dburl/dsn.go @@ -330,8 +330,7 @@ func GenFirebird(u *URL) (string, error) { RawQuery: u.RawQuery, Fragment: u.Fragment, } - - return z.String(), nil + return strings.TrimPrefix(z.String(), "//"), nil } // GenADODB generates a adodb DSN from the passed URL. diff --git a/vendor/github.com/xo/dburl/go.sum b/vendor/github.com/xo/dburl/go.sum new file mode 100644 index 000000000..e69de29bb diff --git a/vendor/github.com/xo/tblfmt/go.mod b/vendor/github.com/xo/tblfmt/go.mod index f66af95c8..b8dc613f2 100644 --- a/vendor/github.com/xo/tblfmt/go.mod +++ b/vendor/github.com/xo/tblfmt/go.mod @@ -1,6 +1,3 @@ module github.com/xo/tblfmt -require ( - github.com/mattn/go-runewidth v0.0.3 - github.com/xo/dburl v0.0.0-20180921222126-e33971d4c132 -) +require github.com/mattn/go-runewidth v0.0.3 diff --git a/vendor/github.com/xo/tblfmt/go.sum b/vendor/github.com/xo/tblfmt/go.sum index 977b1d669..1c1891604 100644 --- a/vendor/github.com/xo/tblfmt/go.sum +++ b/vendor/github.com/xo/tblfmt/go.sum @@ -1,4 +1,2 @@ github.com/mattn/go-runewidth v0.0.3 h1:a+kO+98RDGEfo6asOGMmpodZq4FNtnGP54yps8BzLR4= github.com/mattn/go-runewidth v0.0.3/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU= -github.com/xo/dburl v0.0.0-20180921222126-e33971d4c132 h1:cRKJ4yZeCZbCEXJmjZMa9s2z+3eavo2a4qu/usvVopI= -github.com/xo/dburl v0.0.0-20180921222126-e33971d4c132/go.mod h1:g6rdekR8vgfVZrkLWfobLTm0kVez7GAN23mWtkGCJ14= diff --git a/vendor/golang.org/x/net/internal/socket/empty.s b/vendor/golang.org/x/net/internal/socket/empty.s new file mode 100644 index 000000000..bff0231c7 --- /dev/null +++ b/vendor/golang.org/x/net/internal/socket/empty.s @@ -0,0 +1,7 @@ +// Copyright 2018 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. + +// +build darwin,go1.12 + +// This exists solely so we can linkname in symbols from syscall. diff --git a/vendor/golang.org/x/net/internal/socket/sys_go1_11_darwin.go b/vendor/golang.org/x/net/internal/socket/sys_go1_11_darwin.go new file mode 100644 index 000000000..02d2b3cc8 --- /dev/null +++ b/vendor/golang.org/x/net/internal/socket/sys_go1_11_darwin.go @@ -0,0 +1,33 @@ +// Copyright 2018 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. + +// +build !go1.12 + +package socket + +import ( + "syscall" + "unsafe" +) + +func getsockopt(s uintptr, level, name int, b []byte) (int, error) { + l := uint32(len(b)) + _, _, errno := syscall.Syscall6(syscall.SYS_GETSOCKOPT, s, uintptr(level), uintptr(name), uintptr(unsafe.Pointer(&b[0])), uintptr(unsafe.Pointer(&l)), 0) + return int(l), errnoErr(errno) +} + +func setsockopt(s uintptr, level, name int, b []byte) error { + _, _, errno := syscall.Syscall6(syscall.SYS_SETSOCKOPT, s, uintptr(level), uintptr(name), uintptr(unsafe.Pointer(&b[0])), uintptr(len(b)), 0) + return errnoErr(errno) +} + +func recvmsg(s uintptr, h *msghdr, flags int) (int, error) { + n, _, errno := syscall.Syscall(syscall.SYS_RECVMSG, s, uintptr(unsafe.Pointer(h)), uintptr(flags)) + return int(n), errnoErr(errno) +} + +func sendmsg(s uintptr, h *msghdr, flags int) (int, error) { + n, _, errno := syscall.Syscall(syscall.SYS_SENDMSG, s, uintptr(unsafe.Pointer(h)), uintptr(flags)) + return int(n), errnoErr(errno) +} diff --git a/vendor/golang.org/x/net/internal/socket/sys_go1_12_darwin.go b/vendor/golang.org/x/net/internal/socket/sys_go1_12_darwin.go new file mode 100644 index 000000000..0999a19fb --- /dev/null +++ b/vendor/golang.org/x/net/internal/socket/sys_go1_12_darwin.go @@ -0,0 +1,42 @@ +// Copyright 2018 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. + +// +build go1.12 + +package socket + +import ( + "syscall" + "unsafe" +) + +//go:linkname syscall_getsockopt syscall.getsockopt +func syscall_getsockopt(s int, level int, name int, val unsafe.Pointer, vallen *uint32) error + +func getsockopt(s uintptr, level, name int, b []byte) (int, error) { + l := uint32(len(b)) + err := syscall_getsockopt(int(s), level, name, unsafe.Pointer(&b[0]), &l) + return int(l), err +} + +//go:linkname syscall_setsockopt syscall.setsockopt +func syscall_setsockopt(s int, level int, name int, val unsafe.Pointer, vallen uintptr) error + +func setsockopt(s uintptr, level, name int, b []byte) error { + return syscall_setsockopt(int(s), level, name, unsafe.Pointer(&b[0]), uintptr(len(b))) +} + +//go:linkname syscall_recvmsg syscall.recvmsg +func syscall_recvmsg(s int, msg *syscall.Msghdr, flags int) (n int, err error) + +func recvmsg(s uintptr, h *msghdr, flags int) (int, error) { + return syscall_recvmsg(int(s), (*syscall.Msghdr)(unsafe.Pointer(h)), flags) +} + +//go:linkname syscall_sendmsg syscall.sendmsg +func syscall_sendmsg(s int, msg *syscall.Msghdr, flags int) (n int, err error) + +func sendmsg(s uintptr, h *msghdr, flags int) (int, error) { + return syscall_sendmsg(int(s), (*syscall.Msghdr)(unsafe.Pointer(h)), flags) +} diff --git a/vendor/golang.org/x/net/internal/socket/sys_posix.go b/vendor/golang.org/x/net/internal/socket/sys_posix.go index dc130c27e..9a9bc4762 100644 --- a/vendor/golang.org/x/net/internal/socket/sys_posix.go +++ b/vendor/golang.org/x/net/internal/socket/sys_posix.go @@ -121,18 +121,21 @@ var zoneCache = ipv6ZoneCache{ toName: make(map[int]string), } -func (zc *ipv6ZoneCache) update(ift []net.Interface) { +// update refreshes the network interface information if the cache was last +// updated more than 1 minute ago, or if force is set. It returns whether the +// cache was updated. +func (zc *ipv6ZoneCache) update(ift []net.Interface, force bool) (updated bool) { zc.Lock() defer zc.Unlock() now := time.Now() - if zc.lastFetched.After(now.Add(-60 * time.Second)) { - return + if !force && zc.lastFetched.After(now.Add(-60*time.Second)) { + return false } zc.lastFetched = now if len(ift) == 0 { var err error if ift, err = net.Interfaces(); err != nil { - return + return false } } zc.toIndex = make(map[string]int, len(ift)) @@ -143,25 +146,38 @@ func (zc *ipv6ZoneCache) update(ift []net.Interface) { zc.toName[ifi.Index] = ifi.Name } } + return true } func (zc *ipv6ZoneCache) name(zone int) string { - zoneCache.update(nil) + updated := zoneCache.update(nil, false) zoneCache.RLock() - defer zoneCache.RUnlock() name, ok := zoneCache.toName[zone] - if !ok { + zoneCache.RUnlock() + if !ok && !updated { + zoneCache.update(nil, true) + zoneCache.RLock() + name, ok = zoneCache.toName[zone] + zoneCache.RUnlock() + } + if !ok { // last resort name = strconv.Itoa(zone) } return name } func (zc *ipv6ZoneCache) index(zone string) int { - zoneCache.update(nil) + updated := zoneCache.update(nil, false) zoneCache.RLock() - defer zoneCache.RUnlock() index, ok := zoneCache.toIndex[zone] - if !ok { + zoneCache.RUnlock() + if !ok && !updated { + zoneCache.update(nil, true) + zoneCache.RLock() + index, ok = zoneCache.toIndex[zone] + zoneCache.RUnlock() + } + if !ok { // last resort index, _ = strconv.Atoi(zone) } return index diff --git a/vendor/golang.org/x/net/internal/socket/sys_unix.go b/vendor/golang.org/x/net/internal/socket/sys_unix.go index 18eba3085..0eb71283f 100644 --- a/vendor/golang.org/x/net/internal/socket/sys_unix.go +++ b/vendor/golang.org/x/net/internal/socket/sys_unix.go @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -// +build darwin dragonfly freebsd linux,!s390x,!386 netbsd openbsd +// +build dragonfly freebsd linux,!s390x,!386 netbsd openbsd package socket diff --git a/vendor/golang.org/x/net/ipv4/doc.go b/vendor/golang.org/x/net/ipv4/doc.go index 3efa29037..863d55b8d 100644 --- a/vendor/golang.org/x/net/ipv4/doc.go +++ b/vendor/golang.org/x/net/ipv4/doc.go @@ -241,4 +241,5 @@ // IncludeSourceSpecificGroup may return an error. package ipv4 // import "golang.org/x/net/ipv4" -// BUG(mikio): This package is not implemented on JS, NaCl and Plan 9. +// BUG(mikio): This package is not implemented on AIX, JS, NaCl and +// Plan 9. diff --git a/vendor/golang.org/x/net/ipv4/payload_cmsg.go b/vendor/golang.org/x/net/ipv4/payload_cmsg.go index 204a49fea..a7c892dc4 100644 --- a/vendor/golang.org/x/net/ipv4/payload_cmsg.go +++ b/vendor/golang.org/x/net/ipv4/payload_cmsg.go @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -// +build !js,!nacl,!plan9,!windows +// +build darwin dragonfly freebsd linux netbsd openbsd solaris package ipv4 diff --git a/vendor/golang.org/x/net/ipv4/payload_cmsg_go1_8.go b/vendor/golang.org/x/net/ipv4/payload_cmsg_go1_8.go index 8d45599f4..15a27b7a0 100644 --- a/vendor/golang.org/x/net/ipv4/payload_cmsg_go1_8.go +++ b/vendor/golang.org/x/net/ipv4/payload_cmsg_go1_8.go @@ -3,7 +3,7 @@ // license that can be found in the LICENSE file. // +build !go1.9 -// +build !js,!nacl,!plan9,!windows +// +build darwin dragonfly freebsd linux netbsd openbsd solaris package ipv4 diff --git a/vendor/golang.org/x/net/ipv4/payload_cmsg_go1_9.go b/vendor/golang.org/x/net/ipv4/payload_cmsg_go1_9.go index 4081aad8b..aab3b224e 100644 --- a/vendor/golang.org/x/net/ipv4/payload_cmsg_go1_9.go +++ b/vendor/golang.org/x/net/ipv4/payload_cmsg_go1_9.go @@ -3,7 +3,7 @@ // license that can be found in the LICENSE file. // +build go1.9 -// +build !js,!nacl,!plan9,!windows +// +build darwin dragonfly freebsd linux netbsd openbsd solaris package ipv4 diff --git a/vendor/golang.org/x/net/ipv4/payload_nocmsg.go b/vendor/golang.org/x/net/ipv4/payload_nocmsg.go index 1d434c61a..d57f05c10 100644 --- a/vendor/golang.org/x/net/ipv4/payload_nocmsg.go +++ b/vendor/golang.org/x/net/ipv4/payload_nocmsg.go @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -// +build js nacl plan9 windows +// +build !darwin,!dragonfly,!freebsd,!linux,!netbsd,!openbsd,!solaris package ipv4 diff --git a/vendor/golang.org/x/net/ipv6/doc.go b/vendor/golang.org/x/net/ipv6/doc.go index e0be9d50d..d38ea0da6 100644 --- a/vendor/golang.org/x/net/ipv6/doc.go +++ b/vendor/golang.org/x/net/ipv6/doc.go @@ -240,4 +240,5 @@ // IncludeSourceSpecificGroup may return an error. package ipv6 // import "golang.org/x/net/ipv6" -// BUG(mikio): This package is not implemented on JS, NaCl and Plan 9. +// BUG(mikio): This package is not implemented on AIX, JS, NaCl and +// Plan 9. diff --git a/vendor/golang.org/x/net/ipv6/payload_cmsg.go b/vendor/golang.org/x/net/ipv6/payload_cmsg.go index 3f23b5d21..e17847d0b 100644 --- a/vendor/golang.org/x/net/ipv6/payload_cmsg.go +++ b/vendor/golang.org/x/net/ipv6/payload_cmsg.go @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -// +build !js,!nacl,!plan9,!windows +// +build darwin dragonfly freebsd linux netbsd openbsd solaris package ipv6 diff --git a/vendor/golang.org/x/net/ipv6/payload_cmsg_go1_8.go b/vendor/golang.org/x/net/ipv6/payload_cmsg_go1_8.go index bc4209db7..a48a6ed64 100644 --- a/vendor/golang.org/x/net/ipv6/payload_cmsg_go1_8.go +++ b/vendor/golang.org/x/net/ipv6/payload_cmsg_go1_8.go @@ -3,7 +3,7 @@ // license that can be found in the LICENSE file. // +build !go1.9 -// +build !js,!nacl,!plan9,!windows +// +build darwin dragonfly freebsd linux netbsd openbsd solaris package ipv6 diff --git a/vendor/golang.org/x/net/ipv6/payload_cmsg_go1_9.go b/vendor/golang.org/x/net/ipv6/payload_cmsg_go1_9.go index 7dd650480..fb196ed80 100644 --- a/vendor/golang.org/x/net/ipv6/payload_cmsg_go1_9.go +++ b/vendor/golang.org/x/net/ipv6/payload_cmsg_go1_9.go @@ -3,7 +3,7 @@ // license that can be found in the LICENSE file. // +build go1.9 -// +build !js,!nacl,!plan9,!windows +// +build darwin dragonfly freebsd linux netbsd openbsd solaris package ipv6 diff --git a/vendor/golang.org/x/net/ipv6/payload_nocmsg.go b/vendor/golang.org/x/net/ipv6/payload_nocmsg.go index 459142d26..bfb544784 100644 --- a/vendor/golang.org/x/net/ipv6/payload_nocmsg.go +++ b/vendor/golang.org/x/net/ipv6/payload_nocmsg.go @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -// +build js nacl plan9 windows +// +build !darwin,!dragonfly,!freebsd,!linux,!netbsd,!openbsd,!solaris package ipv6 diff --git a/vendor/golang.org/x/sys/unix/asm_linux_ppc64x.s b/vendor/golang.org/x/sys/unix/asm_linux_ppc64x.s index 649e58714..88f712557 100644 --- a/vendor/golang.org/x/sys/unix/asm_linux_ppc64x.s +++ b/vendor/golang.org/x/sys/unix/asm_linux_ppc64x.s @@ -15,12 +15,6 @@ // Just jump to package syscall's implementation for all these functions. // The runtime may know about them. -TEXT ·Syscall(SB),NOSPLIT,$0-56 - BR syscall·Syscall(SB) - -TEXT ·Syscall6(SB),NOSPLIT,$0-80 - BR syscall·Syscall6(SB) - TEXT ·SyscallNoError(SB),NOSPLIT,$0-48 BL runtime·entersyscall(SB) MOVD a1+8(FP), R3 @@ -36,12 +30,6 @@ TEXT ·SyscallNoError(SB),NOSPLIT,$0-48 BL runtime·exitsyscall(SB) RET -TEXT ·RawSyscall(SB),NOSPLIT,$0-56 - BR syscall·RawSyscall(SB) - -TEXT ·RawSyscall6(SB),NOSPLIT,$0-80 - BR syscall·RawSyscall6(SB) - TEXT ·RawSyscallNoError(SB),NOSPLIT,$0-48 MOVD a1+8(FP), R3 MOVD a2+16(FP), R4 diff --git a/vendor/golang.org/x/sys/unix/mkerrors.sh b/vendor/golang.org/x/sys/unix/mkerrors.sh index 73e179a89..aa860fa26 100755 --- a/vendor/golang.org/x/sys/unix/mkerrors.sh +++ b/vendor/golang.org/x/sys/unix/mkerrors.sh @@ -101,7 +101,7 @@ includes_DragonFly=' ' includes_FreeBSD=' -#include +#include #include #include #include diff --git a/vendor/golang.org/x/sys/unix/mkpost.go b/vendor/golang.org/x/sys/unix/mkpost.go index 7e5c22c47..6d263cf15 100644 --- a/vendor/golang.org/x/sys/unix/mkpost.go +++ b/vendor/golang.org/x/sys/unix/mkpost.go @@ -46,6 +46,10 @@ func main() { valRegex := regexp.MustCompile(`type (Fsid|Sigset_t) struct {(\s+)X__val(\s+\S+\s+)}`) b = valRegex.ReplaceAll(b, []byte("type $1 struct {${2}Val$3}")) + // Intentionally export __fds_bits field in FdSet + fdSetRegex := regexp.MustCompile(`type (FdSet) struct {(\s+)X__fds_bits(\s+\S+\s+)}`) + b = fdSetRegex.ReplaceAll(b, []byte("type $1 struct {${2}Bits$3}")) + // If we have empty Ptrace structs, we should delete them. Only s390x emits // nonempty Ptrace structs. ptraceRexexp := regexp.MustCompile(`type Ptrace((Psw|Fpregs|Per) struct {\s*})`) @@ -65,6 +69,10 @@ func main() { convertUtsnameRegex := regexp.MustCompile(`((Sys|Node|Domain)name|Release|Version|Machine)(\s+)\[(\d+)\]u?int8`) b = convertUtsnameRegex.ReplaceAll(b, []byte("$1$3[$4]byte")) + // Convert [1024]int8 to [1024]byte in Ptmget members + convertPtmget := regexp.MustCompile(`([SC]n)(\s+)\[(\d+)\]u?int8`) + b = convertPtmget.ReplaceAll(b, []byte("$1[$3]byte")) + // Remove spare fields (e.g. in Statx_t) spareFieldsRegex := regexp.MustCompile(`X__spare\S*`) b = spareFieldsRegex.ReplaceAll(b, []byte("_")) diff --git a/vendor/golang.org/x/sys/unix/syscall_linux.go b/vendor/golang.org/x/sys/unix/syscall_linux.go index 84aa8ea03..96ff70dcf 100644 --- a/vendor/golang.org/x/sys/unix/syscall_linux.go +++ b/vendor/golang.org/x/sys/unix/syscall_linux.go @@ -12,6 +12,8 @@ package unix import ( + "encoding/binary" + "net" "syscall" "unsafe" ) @@ -710,6 +712,51 @@ func (sa *SockaddrXDP) sockaddr() (unsafe.Pointer, _Socklen, error) { return unsafe.Pointer(&sa.raw), SizeofSockaddrXDP, nil } +// This constant mirrors the #define of PX_PROTO_OE in +// linux/if_pppox.h. We're defining this by hand here instead of +// autogenerating through mkerrors.sh because including +// linux/if_pppox.h causes some declaration conflicts with other +// includes (linux/if_pppox.h includes linux/in.h, which conflicts +// with netinet/in.h). Given that we only need a single zero constant +// out of that file, it's cleaner to just define it by hand here. +const px_proto_oe = 0 + +type SockaddrPPPoE struct { + SID uint16 + Remote net.HardwareAddr + Dev string + raw RawSockaddrPPPoX +} + +func (sa *SockaddrPPPoE) sockaddr() (unsafe.Pointer, _Socklen, error) { + if len(sa.Remote) != 6 { + return nil, 0, EINVAL + } + if len(sa.Dev) > IFNAMSIZ-1 { + return nil, 0, EINVAL + } + + *(*uint16)(unsafe.Pointer(&sa.raw[0])) = AF_PPPOX + // This next field is in host-endian byte order. We can't use the + // same unsafe pointer cast as above, because this value is not + // 32-bit aligned and some architectures don't allow unaligned + // access. + // + // However, the value of px_proto_oe is 0, so we can use + // encoding/binary helpers to write the bytes without worrying + // about the ordering. + binary.BigEndian.PutUint32(sa.raw[2:6], px_proto_oe) + // This field is deliberately big-endian, unlike the previous + // one. The kernel expects SID to be in network byte order. + binary.BigEndian.PutUint16(sa.raw[6:8], sa.SID) + copy(sa.raw[8:14], sa.Remote) + for i := 14; i < 14+IFNAMSIZ; i++ { + sa.raw[i] = 0 + } + copy(sa.raw[14:], sa.Dev) + return unsafe.Pointer(&sa.raw), SizeofSockaddrPPPoX, nil +} + func anyToSockaddr(fd int, rsa *RawSockaddrAny) (Sockaddr, error) { switch rsa.Addr.Family { case AF_NETLINK: @@ -820,6 +867,22 @@ func anyToSockaddr(fd int, rsa *RawSockaddrAny) (Sockaddr, error) { SharedUmemFD: pp.Shared_umem_fd, } return sa, nil + case AF_PPPOX: + pp := (*RawSockaddrPPPoX)(unsafe.Pointer(rsa)) + if binary.BigEndian.Uint32(pp[2:6]) != px_proto_oe { + return nil, EINVAL + } + sa := &SockaddrPPPoE{ + SID: binary.BigEndian.Uint16(pp[6:8]), + Remote: net.HardwareAddr(pp[8:14]), + } + for i := 14; i < 14+IFNAMSIZ; i++ { + if pp[i] == 0 { + sa.Dev = string(pp[14:i]) + break + } + } + return sa, nil } return nil, EAFNOSUPPORT } diff --git a/vendor/golang.org/x/sys/unix/syscall_netbsd.go b/vendor/golang.org/x/sys/unix/syscall_netbsd.go index b711aca82..059327a36 100644 --- a/vendor/golang.org/x/sys/unix/syscall_netbsd.go +++ b/vendor/golang.org/x/sys/unix/syscall_netbsd.go @@ -13,6 +13,7 @@ package unix import ( + "runtime" "syscall" "unsafe" ) @@ -190,6 +191,13 @@ func IoctlGetTermios(fd int, req uint) (*Termios, error) { return &value, err } +func IoctlGetPtmget(fd int, req uint) (*Ptmget, error) { + var value Ptmget + err := ioctl(fd, req, uintptr(unsafe.Pointer(&value))) + runtime.KeepAlive(value) + return &value, err +} + func Uname(uname *Utsname) error { mib := []_C_int{CTL_KERN, KERN_OSTYPE} n := unsafe.Sizeof(uname.Sysname) diff --git a/vendor/golang.org/x/sys/unix/syscall_unix_gc.go b/vendor/golang.org/x/sys/unix/syscall_unix_gc.go index 4cb8e8edf..1c70d1b69 100644 --- a/vendor/golang.org/x/sys/unix/syscall_unix_gc.go +++ b/vendor/golang.org/x/sys/unix/syscall_unix_gc.go @@ -3,7 +3,7 @@ // license that can be found in the LICENSE file. // +build darwin dragonfly freebsd linux netbsd openbsd solaris -// +build !gccgo +// +build !gccgo,!ppc64le,!ppc64 package unix diff --git a/vendor/golang.org/x/sys/unix/syscall_unix_gc_ppc64x.go b/vendor/golang.org/x/sys/unix/syscall_unix_gc_ppc64x.go new file mode 100644 index 000000000..86dc765ab --- /dev/null +++ b/vendor/golang.org/x/sys/unix/syscall_unix_gc_ppc64x.go @@ -0,0 +1,24 @@ +// Copyright 2018 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. + +// +build linux +// +build ppc64le ppc64 +// +build !gccgo + +package unix + +import "syscall" + +func Syscall(trap, a1, a2, a3 uintptr) (r1, r2 uintptr, err syscall.Errno) { + return syscall.Syscall(trap, a1, a2, a3) +} +func Syscall6(trap, a1, a2, a3, a4, a5, a6 uintptr) (r1, r2 uintptr, err syscall.Errno) { + return syscall.Syscall6(trap, a1, a2, a3, a4, a5, a6) +} +func RawSyscall(trap, a1, a2, a3 uintptr) (r1, r2 uintptr, err syscall.Errno) { + return syscall.RawSyscall(trap, a1, a2, a3) +} +func RawSyscall6(trap, a1, a2, a3, a4, a5, a6 uintptr) (r1, r2 uintptr, err syscall.Errno) { + return syscall.RawSyscall6(trap, a1, a2, a3, a4, a5, a6) +} diff --git a/vendor/golang.org/x/sys/unix/types_freebsd.go b/vendor/golang.org/x/sys/unix/types_freebsd.go index 8421ccf1c..747079895 100644 --- a/vendor/golang.org/x/sys/unix/types_freebsd.go +++ b/vendor/golang.org/x/sys/unix/types_freebsd.go @@ -26,7 +26,7 @@ package unix #include #include #include -#include +#include #include #include #include diff --git a/vendor/golang.org/x/sys/unix/types_netbsd.go b/vendor/golang.org/x/sys/unix/types_netbsd.go index 1edbf1ba7..2dd4f9542 100644 --- a/vendor/golang.org/x/sys/unix/types_netbsd.go +++ b/vendor/golang.org/x/sys/unix/types_netbsd.go @@ -248,6 +248,8 @@ type Termios C.struct_termios type Winsize C.struct_winsize +type Ptmget C.struct_ptmget + // fchmodat-like syscalls. const ( diff --git a/vendor/golang.org/x/sys/unix/ztypes_freebsd_386.go b/vendor/golang.org/x/sys/unix/ztypes_freebsd_386.go index 28ef5242f..c146c1ad3 100644 --- a/vendor/golang.org/x/sys/unix/ztypes_freebsd_386.go +++ b/vendor/golang.org/x/sys/unix/ztypes_freebsd_386.go @@ -339,7 +339,7 @@ type Kevent_t struct { } type FdSet struct { - _ [32]uint32 + Bits [32]uint32 } const ( diff --git a/vendor/golang.org/x/sys/unix/ztypes_freebsd_amd64.go b/vendor/golang.org/x/sys/unix/ztypes_freebsd_amd64.go index e2d984a48..ac33a8dd4 100644 --- a/vendor/golang.org/x/sys/unix/ztypes_freebsd_amd64.go +++ b/vendor/golang.org/x/sys/unix/ztypes_freebsd_amd64.go @@ -337,7 +337,7 @@ type Kevent_t struct { } type FdSet struct { - _ [16]uint64 + Bits [16]uint64 } const ( diff --git a/vendor/golang.org/x/sys/unix/ztypes_freebsd_arm.go b/vendor/golang.org/x/sys/unix/ztypes_freebsd_arm.go index 9b415aba4..e27511a64 100644 --- a/vendor/golang.org/x/sys/unix/ztypes_freebsd_arm.go +++ b/vendor/golang.org/x/sys/unix/ztypes_freebsd_arm.go @@ -337,7 +337,7 @@ type Kevent_t struct { } type FdSet struct { - _ [32]uint32 + Bits [32]uint32 } const ( diff --git a/vendor/golang.org/x/sys/unix/ztypes_linux_386.go b/vendor/golang.org/x/sys/unix/ztypes_linux_386.go index 5f8f03492..f56e164b7 100644 --- a/vendor/golang.org/x/sys/unix/ztypes_linux_386.go +++ b/vendor/golang.org/x/sys/unix/ztypes_linux_386.go @@ -286,6 +286,8 @@ type RawSockaddrXDP struct { Shared_umem_fd uint32 } +type RawSockaddrPPPoX [0x1e]byte + type RawSockaddr struct { Family uint16 Data [14]int8 @@ -421,6 +423,7 @@ const ( SizeofSockaddrALG = 0x58 SizeofSockaddrVM = 0x10 SizeofSockaddrXDP = 0x10 + SizeofSockaddrPPPoX = 0x1e SizeofLinger = 0x8 SizeofIovec = 0x8 SizeofIPMreq = 0x8 diff --git a/vendor/golang.org/x/sys/unix/ztypes_linux_amd64.go b/vendor/golang.org/x/sys/unix/ztypes_linux_amd64.go index aa52a439d..ac5f636a6 100644 --- a/vendor/golang.org/x/sys/unix/ztypes_linux_amd64.go +++ b/vendor/golang.org/x/sys/unix/ztypes_linux_amd64.go @@ -288,6 +288,8 @@ type RawSockaddrXDP struct { Shared_umem_fd uint32 } +type RawSockaddrPPPoX [0x1e]byte + type RawSockaddr struct { Family uint16 Data [14]int8 @@ -425,6 +427,7 @@ const ( SizeofSockaddrALG = 0x58 SizeofSockaddrVM = 0x10 SizeofSockaddrXDP = 0x10 + SizeofSockaddrPPPoX = 0x1e SizeofLinger = 0x8 SizeofIovec = 0x10 SizeofIPMreq = 0x8 diff --git a/vendor/golang.org/x/sys/unix/ztypes_linux_arm.go b/vendor/golang.org/x/sys/unix/ztypes_linux_arm.go index 23c8438be..eb7562da7 100644 --- a/vendor/golang.org/x/sys/unix/ztypes_linux_arm.go +++ b/vendor/golang.org/x/sys/unix/ztypes_linux_arm.go @@ -289,6 +289,8 @@ type RawSockaddrXDP struct { Shared_umem_fd uint32 } +type RawSockaddrPPPoX [0x1e]byte + type RawSockaddr struct { Family uint16 Data [14]uint8 @@ -424,6 +426,7 @@ const ( SizeofSockaddrALG = 0x58 SizeofSockaddrVM = 0x10 SizeofSockaddrXDP = 0x10 + SizeofSockaddrPPPoX = 0x1e SizeofLinger = 0x8 SizeofIovec = 0x8 SizeofIPMreq = 0x8 diff --git a/vendor/golang.org/x/sys/unix/ztypes_linux_arm64.go b/vendor/golang.org/x/sys/unix/ztypes_linux_arm64.go index d7a993e25..3c4fb88d7 100644 --- a/vendor/golang.org/x/sys/unix/ztypes_linux_arm64.go +++ b/vendor/golang.org/x/sys/unix/ztypes_linux_arm64.go @@ -289,6 +289,8 @@ type RawSockaddrXDP struct { Shared_umem_fd uint32 } +type RawSockaddrPPPoX [0x1e]byte + type RawSockaddr struct { Family uint16 Data [14]int8 @@ -426,6 +428,7 @@ const ( SizeofSockaddrALG = 0x58 SizeofSockaddrVM = 0x10 SizeofSockaddrXDP = 0x10 + SizeofSockaddrPPPoX = 0x1e SizeofLinger = 0x8 SizeofIovec = 0x10 SizeofIPMreq = 0x8 diff --git a/vendor/golang.org/x/sys/unix/ztypes_linux_mips.go b/vendor/golang.org/x/sys/unix/ztypes_linux_mips.go index b8c3d0a4d..647e40a77 100644 --- a/vendor/golang.org/x/sys/unix/ztypes_linux_mips.go +++ b/vendor/golang.org/x/sys/unix/ztypes_linux_mips.go @@ -287,6 +287,8 @@ type RawSockaddrXDP struct { Shared_umem_fd uint32 } +type RawSockaddrPPPoX [0x1e]byte + type RawSockaddr struct { Family uint16 Data [14]int8 @@ -422,6 +424,7 @@ const ( SizeofSockaddrALG = 0x58 SizeofSockaddrVM = 0x10 SizeofSockaddrXDP = 0x10 + SizeofSockaddrPPPoX = 0x1e SizeofLinger = 0x8 SizeofIovec = 0x8 SizeofIPMreq = 0x8 diff --git a/vendor/golang.org/x/sys/unix/ztypes_linux_mips64.go b/vendor/golang.org/x/sys/unix/ztypes_linux_mips64.go index a6f76149a..e0159b01d 100644 --- a/vendor/golang.org/x/sys/unix/ztypes_linux_mips64.go +++ b/vendor/golang.org/x/sys/unix/ztypes_linux_mips64.go @@ -289,6 +289,8 @@ type RawSockaddrXDP struct { Shared_umem_fd uint32 } +type RawSockaddrPPPoX [0x1e]byte + type RawSockaddr struct { Family uint16 Data [14]int8 @@ -426,6 +428,7 @@ const ( SizeofSockaddrALG = 0x58 SizeofSockaddrVM = 0x10 SizeofSockaddrXDP = 0x10 + SizeofSockaddrPPPoX = 0x1e SizeofLinger = 0x8 SizeofIovec = 0x10 SizeofIPMreq = 0x8 diff --git a/vendor/golang.org/x/sys/unix/ztypes_linux_mips64le.go b/vendor/golang.org/x/sys/unix/ztypes_linux_mips64le.go index 3dd194176..c1a024dfd 100644 --- a/vendor/golang.org/x/sys/unix/ztypes_linux_mips64le.go +++ b/vendor/golang.org/x/sys/unix/ztypes_linux_mips64le.go @@ -289,6 +289,8 @@ type RawSockaddrXDP struct { Shared_umem_fd uint32 } +type RawSockaddrPPPoX [0x1e]byte + type RawSockaddr struct { Family uint16 Data [14]int8 @@ -426,6 +428,7 @@ const ( SizeofSockaddrALG = 0x58 SizeofSockaddrVM = 0x10 SizeofSockaddrXDP = 0x10 + SizeofSockaddrPPPoX = 0x1e SizeofLinger = 0x8 SizeofIovec = 0x10 SizeofIPMreq = 0x8 diff --git a/vendor/golang.org/x/sys/unix/ztypes_linux_mipsle.go b/vendor/golang.org/x/sys/unix/ztypes_linux_mipsle.go index 210de76cc..7e525eb7f 100644 --- a/vendor/golang.org/x/sys/unix/ztypes_linux_mipsle.go +++ b/vendor/golang.org/x/sys/unix/ztypes_linux_mipsle.go @@ -287,6 +287,8 @@ type RawSockaddrXDP struct { Shared_umem_fd uint32 } +type RawSockaddrPPPoX [0x1e]byte + type RawSockaddr struct { Family uint16 Data [14]int8 @@ -422,6 +424,7 @@ const ( SizeofSockaddrALG = 0x58 SizeofSockaddrVM = 0x10 SizeofSockaddrXDP = 0x10 + SizeofSockaddrPPPoX = 0x1e SizeofLinger = 0x8 SizeofIovec = 0x8 SizeofIPMreq = 0x8 diff --git a/vendor/golang.org/x/sys/unix/ztypes_linux_ppc64.go b/vendor/golang.org/x/sys/unix/ztypes_linux_ppc64.go index b46d54e37..85ae2954d 100644 --- a/vendor/golang.org/x/sys/unix/ztypes_linux_ppc64.go +++ b/vendor/golang.org/x/sys/unix/ztypes_linux_ppc64.go @@ -290,6 +290,8 @@ type RawSockaddrXDP struct { Shared_umem_fd uint32 } +type RawSockaddrPPPoX [0x1e]byte + type RawSockaddr struct { Family uint16 Data [14]uint8 @@ -427,6 +429,7 @@ const ( SizeofSockaddrALG = 0x58 SizeofSockaddrVM = 0x10 SizeofSockaddrXDP = 0x10 + SizeofSockaddrPPPoX = 0x1e SizeofLinger = 0x8 SizeofIovec = 0x10 SizeofIPMreq = 0x8 diff --git a/vendor/golang.org/x/sys/unix/ztypes_linux_ppc64le.go b/vendor/golang.org/x/sys/unix/ztypes_linux_ppc64le.go index 6ee799cef..d0c930a13 100644 --- a/vendor/golang.org/x/sys/unix/ztypes_linux_ppc64le.go +++ b/vendor/golang.org/x/sys/unix/ztypes_linux_ppc64le.go @@ -290,6 +290,8 @@ type RawSockaddrXDP struct { Shared_umem_fd uint32 } +type RawSockaddrPPPoX [0x1e]byte + type RawSockaddr struct { Family uint16 Data [14]uint8 @@ -427,6 +429,7 @@ const ( SizeofSockaddrALG = 0x58 SizeofSockaddrVM = 0x10 SizeofSockaddrXDP = 0x10 + SizeofSockaddrPPPoX = 0x1e SizeofLinger = 0x8 SizeofIovec = 0x10 SizeofIPMreq = 0x8 diff --git a/vendor/golang.org/x/sys/unix/ztypes_linux_riscv64.go b/vendor/golang.org/x/sys/unix/ztypes_linux_riscv64.go index 60ae71e62..c1a20bcd3 100644 --- a/vendor/golang.org/x/sys/unix/ztypes_linux_riscv64.go +++ b/vendor/golang.org/x/sys/unix/ztypes_linux_riscv64.go @@ -289,6 +289,8 @@ type RawSockaddrXDP struct { Shared_umem_fd uint32 } +type RawSockaddrPPPoX [0x1e]byte + type RawSockaddr struct { Family uint16 Data [14]uint8 @@ -426,6 +428,7 @@ const ( SizeofSockaddrALG = 0x58 SizeofSockaddrVM = 0x10 SizeofSockaddrXDP = 0x10 + SizeofSockaddrPPPoX = 0x1e SizeofLinger = 0x8 SizeofIovec = 0x10 SizeofIPMreq = 0x8 diff --git a/vendor/golang.org/x/sys/unix/ztypes_linux_s390x.go b/vendor/golang.org/x/sys/unix/ztypes_linux_s390x.go index dea88f7bb..3c26ea82b 100644 --- a/vendor/golang.org/x/sys/unix/ztypes_linux_s390x.go +++ b/vendor/golang.org/x/sys/unix/ztypes_linux_s390x.go @@ -288,6 +288,8 @@ type RawSockaddrXDP struct { Shared_umem_fd uint32 } +type RawSockaddrPPPoX [0x1e]byte + type RawSockaddr struct { Family uint16 Data [14]int8 @@ -425,6 +427,7 @@ const ( SizeofSockaddrALG = 0x58 SizeofSockaddrVM = 0x10 SizeofSockaddrXDP = 0x10 + SizeofSockaddrPPPoX = 0x1e SizeofLinger = 0x8 SizeofIovec = 0x10 SizeofIPMreq = 0x8 diff --git a/vendor/golang.org/x/sys/unix/ztypes_netbsd_386.go b/vendor/golang.org/x/sys/unix/ztypes_netbsd_386.go index 1fdc5fd21..2dae0c17a 100644 --- a/vendor/golang.org/x/sys/unix/ztypes_netbsd_386.go +++ b/vendor/golang.org/x/sys/unix/ztypes_netbsd_386.go @@ -402,6 +402,13 @@ type Winsize struct { Ypixel uint16 } +type Ptmget struct { + Cfd int32 + Sfd int32 + Cn [1024]byte + Sn [1024]byte +} + const ( AT_FDCWD = -0x64 AT_SYMLINK_NOFOLLOW = 0x200 diff --git a/vendor/golang.org/x/sys/unix/ztypes_netbsd_amd64.go b/vendor/golang.org/x/sys/unix/ztypes_netbsd_amd64.go index 711f78067..1f0e76c0c 100644 --- a/vendor/golang.org/x/sys/unix/ztypes_netbsd_amd64.go +++ b/vendor/golang.org/x/sys/unix/ztypes_netbsd_amd64.go @@ -409,6 +409,13 @@ type Winsize struct { Ypixel uint16 } +type Ptmget struct { + Cfd int32 + Sfd int32 + Cn [1024]byte + Sn [1024]byte +} + const ( AT_FDCWD = -0x64 AT_SYMLINK_NOFOLLOW = 0x200 diff --git a/vendor/golang.org/x/sys/unix/ztypes_netbsd_arm.go b/vendor/golang.org/x/sys/unix/ztypes_netbsd_arm.go index fa1a16bae..53f2159c7 100644 --- a/vendor/golang.org/x/sys/unix/ztypes_netbsd_arm.go +++ b/vendor/golang.org/x/sys/unix/ztypes_netbsd_arm.go @@ -407,6 +407,13 @@ type Winsize struct { Ypixel uint16 } +type Ptmget struct { + Cfd int32 + Sfd int32 + Cn [1024]byte + Sn [1024]byte +} + const ( AT_FDCWD = -0x64 AT_SYMLINK_NOFOLLOW = 0x200 From 666145217f26bbf19075c984b8c48b575817d493 Mon Sep 17 00:00:00 2001 From: Qi Xiao Date: Mon, 19 Nov 2018 18:20:48 +0800 Subject: [PATCH 02/24] SecureGateway skeleton --- cmd/cql-secure-gateway/casbin/adapter.go | 203 ++++++++ cmd/cql-secure-gateway/casbin/adapter_test.go | 406 ++++++++++++++++ cmd/cql-secure-gateway/casbin/casbin.go | 55 +++ cmd/cql-secure-gateway/cursor.go | 443 ++++++++++++++++++ cmd/cql-secure-gateway/errors.go | 31 ++ cmd/cql-secure-gateway/main.go | 93 ++++ cmd/cql-secure-gateway/scanner.go | 106 +++++ cmd/cql-secure-gateway/server.go | 80 ++++ cmd/cql-secure-gateway/utils.go | 58 +++ 9 files changed, 1475 insertions(+) create mode 100644 cmd/cql-secure-gateway/casbin/adapter.go create mode 100644 cmd/cql-secure-gateway/casbin/adapter_test.go create mode 100644 cmd/cql-secure-gateway/casbin/casbin.go create mode 100644 cmd/cql-secure-gateway/cursor.go create mode 100644 cmd/cql-secure-gateway/errors.go create mode 100644 cmd/cql-secure-gateway/main.go create mode 100644 cmd/cql-secure-gateway/scanner.go create mode 100644 cmd/cql-secure-gateway/server.go create mode 100644 cmd/cql-secure-gateway/utils.go diff --git a/cmd/cql-secure-gateway/casbin/adapter.go b/cmd/cql-secure-gateway/casbin/adapter.go new file mode 100644 index 000000000..009160bd1 --- /dev/null +++ b/cmd/cql-secure-gateway/casbin/adapter.go @@ -0,0 +1,203 @@ +/* + * Copyright 2018 The CovenantSQL Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package casbin + +import ( + "fmt" + "strings" + + "github.com/casbin/casbin/model" + "github.com/casbin/casbin/persist" + "github.com/pkg/errors" +) + +var ( + // ErrNotSupported defines error for policy persistence adapter not supported feature. + ErrNotSupported = errors.New("not supported") + // ErrInvalidField defines error for invalid field description. + ErrInvalidField = errors.New("invalid field description") + // ErrEmptyConfigItem defines empty config item in policies. + ErrEmptyConfigItem = errors.New("empty config item") +) + +// Policy defines single resource policy. +type Policy struct { + User string `yaml:"User"` + Field string `yaml:"Field"` + Action string `yaml:"Action"` +} + +// UserGroup defines single user group relation. +type UserGroup map[string][]string + +// FieldGroup defines single field group relation. +type FieldGroup map[string][]string + +// Config defines the rules config for casbin adapter. +type Config struct { + Policies []Policy `yaml:"Policies"` + UserGroup UserGroup `yaml:"UserGroups"` + FieldGroup FieldGroup `yaml:"FieldGroups"` +} + +// Adapter defines a adapter for casbin to access rules. +type Adapter struct { + cfg *Config +} + +// NewAdapter returns an adapter for casbin enforcer rules. +func NewAdapter(cfg *Config) (a *Adapter, err error) { + if cfg == nil { + err = errors.Wrapf(ErrEmptyConfigItem, "nil config") + return + } + + // valid config + a = &Adapter{ + cfg: cfg, + } + + if err = a.validate(); err != nil { + a = nil + } + + return +} + +func (a *Adapter) validate() (err error) { + // validate non-group fields + for _, p := range a.cfg.Policies { + if p.User == "" || p.Field == "" || p.Action == "" { + err = errors.Wrapf(ErrEmptyConfigItem, "%#v contains empty field", p) + return + } + + if _, exists := a.cfg.FieldGroup[p.Field]; !exists { + // must be a valid field + if err = a.validateField(p.Field); err != nil { + return + } + } + } + + for userGroup, users := range a.cfg.UserGroup { + if userGroup == "" { + err = errors.Wrapf(ErrEmptyConfigItem, "user group name should not be empty") + return + } + + for _, u := range users { + if u == "" { + err = errors.Wrapf(ErrEmptyConfigItem, "user name should not be empty in group: %s", userGroup) + return + } + } + } + + // validate fields + for fieldGroup, fields := range a.cfg.FieldGroup { + if fieldGroup == "" { + err = errors.Wrapf(ErrEmptyConfigItem, "field group name should not be empty") + return + } + + // should all be valid field + for _, f := range fields { + if f == "" { + err = errors.Wrapf(ErrEmptyConfigItem, "field name should not be empty") + return + } + + if err = a.validateField(f); err != nil { + return + } + } + } + + return +} + +func (a *Adapter) validateField(f string) (err error) { + parts := strings.Split(f, ".") + + if len(parts) != 3 { + err = errors.Wrapf(ErrInvalidField, + "invalid field: %s, should be in form \"..\", wildcard is supported", f) + return + } + + for _, p := range parts { + p = strings.TrimSpace(p) + + if p == "" { + err = errors.Wrapf(ErrInvalidField, + "invalid field: %s, /
/ parts should not be blanked", f) + return + } + } + + return +} + +// LoadPolicy implements casbin persist.Adapter interface. +func (a *Adapter) LoadPolicy(model model.Model) (err error) { + // build casbin csv policy and load + + // load policy + for _, p := range a.cfg.Policies { + line := fmt.Sprintf("p, %s, %s, %s", p.User, p.Field, p.Action) + persist.LoadPolicyLine(line, model) + } + + // load user groups + for userGroup, users := range a.cfg.UserGroup { + for _, user := range users { + line := fmt.Sprintf("g, %s, %s", user, userGroup) + persist.LoadPolicyLine(line, model) + } + } + + // load field groups + for fieldGroup, fields := range a.cfg.FieldGroup { + for _, field := range fields { + line := fmt.Sprintf("g2, %s, %s", field, fieldGroup) + persist.LoadPolicyLine(line, model) + } + } + + return +} + +// SavePolicy mocks casbin persist.Adapter interface. +func (*Adapter) SavePolicy(model model.Model) error { + return ErrNotSupported +} + +// AddPolicy mocks casbin persist.Adapter interface. +func (*Adapter) AddPolicy(sec string, ptype string, rule []string) error { + return ErrNotSupported +} + +// RemovePolicy mocks casbin persist.Adapter interface. +func (*Adapter) RemovePolicy(sec string, ptype string, rule []string) error { + return ErrNotSupported +} + +// RemoveFilteredPolicy mocks casbin persist.Adapter interface. +func (*Adapter) RemoveFilteredPolicy(sec string, ptype string, fieldIndex int, fieldValues ...string) error { + return ErrNotSupported +} diff --git a/cmd/cql-secure-gateway/casbin/adapter_test.go b/cmd/cql-secure-gateway/casbin/adapter_test.go new file mode 100644 index 000000000..8f313e538 --- /dev/null +++ b/cmd/cql-secure-gateway/casbin/adapter_test.go @@ -0,0 +1,406 @@ +/* + * Copyright 2018 The CovenantSQL Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package casbin + +import ( + "testing" + + "github.com/casbin/casbin" + "github.com/pkg/errors" + . "github.com/smartystreets/goconvey/convey" +) + +func TestCasbin(t *testing.T) { + Convey("test casbin", t, func(c C) { + testEnforce := func(e *casbin.Enforcer, sub string, obj interface{}, act string, res bool) { + c.So(e.Enforce(sub, obj, act), ShouldEqual, res) + } + + cfg := &Config{ + Policies: []Policy{ + { + User: "alice", + Field: "db1.tbl1.col1", + Action: "read", + }, + { + User: "bob", + Field: "db1.tbl2.col1", + Action: "write", + }, + { + User: "data_group_admin", + Field: "field_group", + Action: "write", + }, + }, + UserGroup: map[string][]string{ + "data_group_admin": { + "alice", + }, + }, + FieldGroup: map[string][]string{ + "field_group": { + "db1.tbl1.col1", + "db1.tbl2.col1", + }, + }, + } + + e, err := NewCasbin(cfg) + So(err, ShouldBeNil) + + testEnforce(e, "alice", "db1.tbl1.col1", "read", true) + testEnforce(e, "alice", "db1.tbl1.col1", "write", true) + testEnforce(e, "alice", "db1.tbl2.col1", "read", false) + testEnforce(e, "alice", "db1.tbl2.col1", "write", true) + testEnforce(e, "bob", "db1.tbl1.col1", "read", false) + testEnforce(e, "bob", "db1.tbl1.col1", "write", false) + testEnforce(e, "bob", "db1.tbl2.col1", "read", false) + testEnforce(e, "bob", "db1.tbl2.col1", "write", true) + }) + Convey("test invalid field config", t, func() { + // nil config + _, err := NewCasbin(nil) + So(err, ShouldNotBeNil) + So(errors.Cause(err), ShouldEqual, ErrEmptyConfigItem) + + // correct control group + cfg := &Config{ + Policies: []Policy{ + { + User: "user1", + Field: "db1.tbl1.col1", + Action: "read", + }, + }, + } + _, err = NewCasbin(cfg) + So(err, ShouldBeNil) + + // empty user field + cfg = &Config{ + Policies: []Policy{ + { + User: "", + Field: "db1.tbl1.col1", + Action: "read", + }, + }, + } + + _, err = NewCasbin(cfg) + So(err, ShouldNotBeNil) + So(errors.Cause(err), ShouldEqual, ErrEmptyConfigItem) + + // empty field + cfg = &Config{ + Policies: []Policy{ + { + User: "user1", + Field: "", + Action: "read", + }, + }, + } + _, err = NewCasbin(cfg) + So(err, ShouldNotBeNil) + So(errors.Cause(err), ShouldEqual, ErrEmptyConfigItem) + + // empty action + cfg = &Config{ + Policies: []Policy{ + { + User: "user1", + Field: "db1.tbl1.col1", + Action: "", + }, + }, + } + _, err = NewCasbin(cfg) + So(err, ShouldNotBeNil) + So(errors.Cause(err), ShouldEqual, ErrEmptyConfigItem) + + // invalid field, not enough field parts + cfg = &Config{ + Policies: []Policy{ + { + User: "user1", + Field: "db1", + Action: "read", + }, + }, + } + _, err = NewCasbin(cfg) + So(err, ShouldNotBeNil) + So(errors.Cause(err), ShouldEqual, ErrInvalidField) + + // invalid field, more than required field parts + cfg = &Config{ + Policies: []Policy{ + { + User: "user1", + Field: "db1.tbl1.col1.what?", + Action: "read", + }, + }, + } + _, err = NewCasbin(cfg) + So(err, ShouldNotBeNil) + So(errors.Cause(err), ShouldEqual, ErrInvalidField) + + // invalid field, contains empty field part + cfg = &Config{ + Policies: []Policy{ + { + User: "user1", + Field: "db1..col1", + Action: "read", + }, + }, + } + _, err = NewCasbin(cfg) + So(err, ShouldNotBeNil) + So(errors.Cause(err), ShouldEqual, ErrInvalidField) + + // correct control group with user groups + cfg = &Config{ + Policies: []Policy{ + { + User: "a", + Field: "fg1", + Action: "read", + }, + }, + UserGroup: map[string][]string{ + "a": { + "user1", + }, + "b": { + "user1", + }, + }, + FieldGroup: map[string][]string{ + "fg1": { + "db1.tbl1.col1", + }, + "fg2": { + "db1.tbl1.col1", + }, + }, + } + _, err = NewCasbin(cfg) + So(err, ShouldBeNil) + + // empty group name in user groups + cfg = &Config{ + Policies: []Policy{ + { + User: "a", + Field: "fg1", + Action: "read", + }, + }, + UserGroup: map[string][]string{ + "a": { + "user1", + }, + "": { + "user1", + }, + }, + FieldGroup: map[string][]string{ + "fg1": { + "db1.tbl1.col1", + }, + "fg2": { + "db1.tbl1.col1", + }, + }, + } + _, err = NewCasbin(cfg) + So(err, ShouldNotBeNil) + So(errors.Cause(err), ShouldEqual, ErrEmptyConfigItem) + + // empty group name in field groups + cfg = &Config{ + Policies: []Policy{ + { + User: "a", + Field: "fg1", + Action: "read", + }, + }, + UserGroup: map[string][]string{ + "a": { + "user1", + }, + "b": { + "user1", + }, + }, + FieldGroup: map[string][]string{ + "fg1": { + "db1.tbl1.col1", + }, + "": { + "db1.tbl1.col1", + }, + }, + } + _, err = NewCasbin(cfg) + So(err, ShouldNotBeNil) + So(errors.Cause(err), ShouldEqual, ErrEmptyConfigItem) + + // empty user name in user groups + cfg = &Config{ + Policies: []Policy{ + { + User: "a", + Field: "fg1", + Action: "read", + }, + }, + UserGroup: map[string][]string{ + "a": { + "user1", + "", + }, + "b": { + "user1", + }, + }, + FieldGroup: map[string][]string{ + "fg1": { + "db1.tbl1.col1", + }, + "fg2": { + "db1.tbl1.col1", + }, + }, + } + _, err = NewCasbin(cfg) + So(err, ShouldNotBeNil) + So(errors.Cause(err), ShouldEqual, ErrEmptyConfigItem) + + // empty field name in field groups + cfg = &Config{ + Policies: []Policy{ + { + User: "a", + Field: "fg1", + Action: "read", + }, + }, + UserGroup: map[string][]string{ + "a": { + "user1", + }, + "b": { + "user1", + }, + }, + FieldGroup: map[string][]string{ + "fg1": { + "db1.tbl1.col1", + "", + }, + "fg2": { + "db1.tbl1.col1", + }, + }, + } + _, err = NewCasbin(cfg) + So(err, ShouldNotBeNil) + So(errors.Cause(err), ShouldEqual, ErrEmptyConfigItem) + + // unknown field group + cfg = &Config{ + Policies: []Policy{ + { + User: "a", + Field: "fg3", + Action: "read", + }, + }, + UserGroup: map[string][]string{ + "a": { + "user1", + }, + "b": { + "user1", + }, + }, + FieldGroup: map[string][]string{ + "fg1": { + "db1.tbl1.col1", + }, + "fg2": { + "db1.tbl1.col1", + }, + }, + } + _, err = NewCasbin(cfg) + So(err, ShouldNotBeNil) + So(errors.Cause(err), ShouldEqual, ErrInvalidField) + + // invalid field in field group + cfg = &Config{ + Policies: []Policy{ + { + User: "a", + Field: "fg1", + Action: "read", + }, + }, + UserGroup: map[string][]string{ + "a": { + "user1", + }, + "b": { + "user1", + }, + }, + FieldGroup: map[string][]string{ + "fg1": { + "db1.tbl1.col1", + "db1", + }, + "fg2": { + "db1.tbl1.col1", + }, + }, + } + _, err = NewCasbin(cfg) + So(err, ShouldNotBeNil) + So(errors.Cause(err), ShouldEqual, ErrInvalidField) + }) + Convey("unsupported features", t, func() { + a, err := NewAdapter(&Config{}) + So(err, ShouldBeNil) + So(a, ShouldNotBeNil) + // call unsupported fields + err = a.SavePolicy(nil) + So(err, ShouldEqual, ErrNotSupported) + err = a.AddPolicy("", "", []string{}) + So(err, ShouldEqual, ErrNotSupported) + err = a.RemovePolicy("", "", []string{}) + So(err, ShouldEqual, ErrNotSupported) + err = a.RemoveFilteredPolicy("", "", 0) + So(err, ShouldEqual, ErrNotSupported) + }) +} diff --git a/cmd/cql-secure-gateway/casbin/casbin.go b/cmd/cql-secure-gateway/casbin/casbin.go new file mode 100644 index 000000000..584307a9d --- /dev/null +++ b/cmd/cql-secure-gateway/casbin/casbin.go @@ -0,0 +1,55 @@ +/* + * Copyright 2018 The CovenantSQL Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package casbin + +import ( + "github.com/casbin/casbin" + "github.com/casbin/casbin/model" + "github.com/casbin/casbin/persist" +) + +const modelConf = ` +[request_definition] +r = sub, obj, act + +[policy_definition] +p = sub, obj, act + +[role_definition] +g = _, _ +g2 = _, _ + +[policy_effect] +e = some(where (p.eft == allow)) + +[matchers] +m = g(r.sub, p.sub) && g2(r.obj, p.obj) && r.act == p.act` + +// NewCasbin returns a casbin enforcer instance with +// fixed rbac_model_with_resource_roles model and custom structured config. +func NewCasbin(cfg *Config) (e *casbin.Enforcer, err error) { + var ( + m model.Model + rule persist.Adapter + ) + m = casbin.NewModel(modelConf) + if rule, err = NewAdapter(cfg); err != nil { + return + } + e = casbin.NewEnforcer(m, rule) + return +} diff --git a/cmd/cql-secure-gateway/cursor.go b/cmd/cql-secure-gateway/cursor.go new file mode 100644 index 000000000..950b0a008 --- /dev/null +++ b/cmd/cql-secure-gateway/cursor.go @@ -0,0 +1,443 @@ +/* + * Copyright 2018 The CovenantSQL Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package main + +import ( + "database/sql" + "fmt" + "path/filepath" + "regexp" + "strings" + "sync" + + "github.com/CovenantSQL/CovenantSQL/client" + "github.com/CovenantSQL/CovenantSQL/utils/log" + my "github.com/siddontang/go-mysql/mysql" +) + +var ( + dbIDRegex = regexp.MustCompile("^[a-zA-Z0-9_\\.]+$") + specialSelectQuery = regexp.MustCompile("^(?i)SELECT\\s+(DATABASE|USER)\\(\\)\\s*;?\\s*$") + emptyResultQuery = regexp.MustCompile("^(?i)\\s*(?:/\\*.*?\\*/)?\\s*(?:SET|ROLLBACK).*$") + emptyResultWithResultSetQuery = regexp.MustCompile("^(?i)\\s*(?:/\\*.*?\\*/)?\\s*(?:(?:SELECT\\s+)?@@(?:\\w+\\.)?|SHOW\\s+WARNINGS).*$") + showVariablesQuery = regexp.MustCompile("^(?i)\\s*(?:/\\*.*?\\*/)?\\s*SHOW\\s+VARIABLES.*$") + showDatabasesQuery = regexp.MustCompile("^(?i)\\s*(?:/\\*.*?\\*/)?\\s*SHOW\\s+DATABASES.*$") + useDatabaseQuery = regexp.MustCompile("^(?i)\\s*USE\\s+`?(\\w+)`?\\s*$") + readQuery = regexp.MustCompile("^(?i)\\s*(?:SELECT|SHOW|DESC)") + mysqlServerVariables = map[string]interface{}{ + "max_allowed_packet": 255 * 255 * 255, + "auto_increment_increment": 1, + "transaction_isolation": "SERIALIZABLE", + "tx_isolation": "SERIALIZABLE", + "transaction_read_only": 0, + "tx_read_only": 0, + "autocommit": 1, + "character_set_server": "utf8", + "collation_server": "utf8_general_ci", + } +) + +// Cursor is a mysql connection handler, like a cursor of normal database. +type Cursor struct { + server *Server + curUser string + curDBLock sync.Mutex + curDB string + curDBInstance *sql.DB +} + +// NewCursor returns a new cursor. +func NewCursor(s *Server) (c *Cursor) { + return &Cursor{server: s} +} + +func (c *Cursor) buildResultSet(rows *sql.Rows) (r *my.Result, err error) { + // get columns + var columns []string + if columns, err = rows.Columns(); err != nil { + err = my.NewError(my.ER_UNKNOWN_ERROR, err.Error()) + return + } + + // read all rows + var resultData [][]interface{} + if resultData, err = readAllRows(rows); err != nil { + err = my.NewError(my.ER_UNKNOWN_ERROR, err.Error()) + return + } + + var resultSet *my.Resultset + if resultSet, err = my.BuildSimpleTextResultset(columns, resultData); err != nil { + err = my.NewError(my.ER_UNKNOWN_ERROR, err.Error()) + return + } + + r = &my.Result{ + Status: 0, + InsertId: 0, + AffectedRows: 0, + Resultset: resultSet, + } + return +} + +func (c *Cursor) ensureDatabase() (conn *sql.DB, err error) { + c.curDBLock.Lock() + defer c.curDBLock.Unlock() + + if c.curDB == "" { + err = my.NewError(my.ER_NO_DB_ERROR, "select database before any query") + return + } + + conn = c.curDBInstance + + return +} + +func (c *Cursor) detectColumnType(typeStr string) (typeByte uint8) { + typeStr = strings.ToUpper(typeStr) + + if strings.Contains(typeStr, "INT") { + return my.MYSQL_TYPE_LONGLONG + } else if strings.Contains(typeStr, "CHAR") || strings.Contains(typeStr, "CLOB") || + strings.Contains(typeStr, "TEXT") { + return my.MYSQL_TYPE_VAR_STRING + } else if strings.Contains(typeStr, "BLOB") || typeStr == "" { + return my.MYSQL_TYPE_LONG_BLOB + } else if strings.Contains(typeStr, "REAL") || strings.Contains(typeStr, "FLOA") || + strings.Contains(typeStr, "DOUB") { + return my.MYSQL_TYPE_DOUBLE + } else if strings.Contains(typeStr, "BOOLEAN") { + return my.MYSQL_TYPE_BIT + } else if strings.Contains(typeStr, "TIMESTAMP") || strings.Contains(typeStr, "DATETIME") { + return my.MYSQL_TYPE_TIMESTAMP + } else if strings.Contains(typeStr, "TIME") { + return my.MYSQL_TYPE_TIME + } else if strings.Contains(typeStr, "DATE") { + return my.MYSQL_TYPE_DATE + } else { + return my.MYSQL_TYPE_LONG_BLOB + } +} + +func (c *Cursor) handleSpecialQuery(query string) (r *my.Result, processed bool, err error) { + if emptyResultQuery.MatchString(query) { // send empty result for variables query/table listing + // return empty result + r = &my.Result{ + Status: 0, + InsertId: 0, + AffectedRows: 0, + Resultset: nil, + } + processed = true + } else if emptyResultWithResultSetQuery.MatchString(query) { // send empty result include non-nil result set + // return empty result with empty result set + var resultSet *my.Resultset + var columns []string + var row []interface{} + + for k, v := range mysqlServerVariables { + if strings.Contains(query, k) { + columns = append(columns, k) + row = append(row, v) + } + } + + if len(columns) == 0 { + columns = append(columns, "_") + } + + if row != nil { + resultSet, _ = my.BuildSimpleTextResultset(columns, [][]interface{}{row}) + } else { + resultSet, _ = my.BuildSimpleTextResultset(columns, [][]interface{}{}) + } + + if resultSet.RowDatas == nil { + // force non-empty result set + resultSet.RowDatas = make([]my.RowData, 0) + } + + r = &my.Result{ + Status: 0, + InsertId: 0, + AffectedRows: 0, + Resultset: resultSet, + } + processed = true + } else if showVariablesQuery.MatchString(query) { // send show variables result with custom config + var rows [][]interface{} + + for k, v := range mysqlServerVariables { + rows = append(rows, []interface{}{k, v}) + } + + resultSet, _ := my.BuildSimpleTextResultset([]string{"Variable_name", "Value"}, rows) + r = &my.Result{ + Status: 0, + InsertId: 0, + AffectedRows: 0, + Resultset: resultSet, + } + processed = true + } else if showDatabasesQuery.MatchString(query) { // send show databases result + // return result including current database + var curDBStr string + c.curDBLock.Lock() + curDBStr = c.curDB + c.curDBLock.Unlock() + + var resultSet *my.Resultset + + if curDBStr != "" { + resultSet, _ = my.BuildSimpleTextResultset([]string{"Database"}, [][]interface{}{{curDBStr}}) + } else { + resultSet, _ = my.BuildSimpleTextResultset([]string{"Database"}, nil) + } + + r = &my.Result{ + Status: 0, + InsertId: 0, + AffectedRows: 0, + Resultset: resultSet, + } + processed = true + } else if matches := useDatabaseQuery.FindStringSubmatch(query); len(matches) > 1 { // use database query, same logic as COM_INIT_DB + dbID := matches[1] + + processed = true + if err = c.UseDB(dbID); err == nil { + r = &my.Result{ + Status: 0, + InsertId: 0, + AffectedRows: 0, + Resultset: nil, + } + } + } else if matches := specialSelectQuery.FindStringSubmatch(query); len(matches) > 1 { + // special select database + // for libmysql trivial implementations + // https://github.com/mysql/mysql-server/blob/4f1d7cf5fcb11a3f84cff27e37100d7295e7d5ca/client/mysql.cc#L4266 + + var resultSet *my.Resultset + + switch strings.ToUpper(matches[1]) { + case "DATABASE": + c.curDBLock.Lock() + resultSet, _ = my.BuildSimpleTextResultset( + []string{"DATABASE()"}, + [][]interface{}{{c.curDB}}, + ) + c.curDBLock.Unlock() + case "USER": + resultSet, _ = my.BuildSimpleTextResultset( + []string{"USER()"}, + [][]interface{}{{c.curUser}}, + ) + } + + r = &my.Result{ + Status: 0, + InsertId: 0, + AffectedRows: 0, + Resultset: resultSet, + } + processed = true + } + + return +} + +// UseDB handle COM_INIT_DB command, you can check whether the dbName is valid, or other. +func (c *Cursor) UseDB(dbName string) (err error) { + c.curDBLock.Lock() + defer c.curDBLock.Unlock() + + // test if the database name is a valid database id + if !dbIDRegex.MatchString(dbName) { + // invalid database + return my.NewError(my.ER_BAD_DB_ERROR, fmt.Sprintf("invalid database: %v", dbName)) + } + + // connect database + cfg := client.NewConfig() + cfg.DatabaseID = dbName + + var db *sql.DB + + if db, err = sql.Open("covenantsql", cfg.FormatDSN()); err != nil { + return + } + + c.curDB = dbName + c.curDBInstance = db + + return +} + +// HandleQuery handle COM_QUERY comamnd, like SELECT, INSERT, UPDATE, etc... +// if Result has a Resultset (SELECT, SHOW, etc...), we will send this as the response, otherwise, we will send Result. +func (c *Cursor) HandleQuery(query string) (r *my.Result, err error) { + var processed bool + + log.WithField("query", query).Info("received query") + + if r, processed, err = c.handleSpecialQuery(query); processed { + return + } + + var conn *sql.DB + + if conn, err = c.ensureDatabase(); err != nil { + return + } + + // as normal query + if readQuery.MatchString(query) { + var rows *sql.Rows + if rows, err = conn.Query(query); err != nil { + err = my.NewError(my.ER_UNKNOWN_ERROR, err.Error()) + return + } + + // build result set + return c.buildResultSet(rows) + } + + var result sql.Result + if result, err = conn.Exec(query); err != nil { + err = my.NewError(my.ER_UNKNOWN_ERROR, err.Error()) + return + } + + lastInsertID, _ := result.LastInsertId() + affectedRows, _ := result.RowsAffected() + + r = &my.Result{ + Status: 0, + InsertId: uint64(lastInsertID), + AffectedRows: uint64(affectedRows), + Resultset: nil, + } + + return +} + +// HandleFieldList handle COM_FILED_LIST command. +func (c *Cursor) HandleFieldList(table string, fieldWildcard string) (fields []*my.Field, err error) { + var conn *sql.DB + + if conn, err = c.ensureDatabase(); err != nil { + return + } + + // send show tables command + var columns *sql.Rows + if columns, err = conn.Query(fmt.Sprintf("DESC `%s`", table)); err != nil { + // wrap error + err = my.NewError(my.ER_UNKNOWN_ERROR, err.Error()) + return + } + + defer columns.Close() + + // transform the sql wildcard to glob pattern + var fieldGlob string + + if fieldWildcard != "" { + fieldGlob = strings.NewReplacer("_", "?", "%", "*").Replace(fieldWildcard) + } + + var cid, defaultValue interface{} + var columnName, typeString string + var isNotNull, isPK bool + + for columns.Next() { + if err = columns.Scan(&cid, &columnName, &typeString, &isNotNull, &defaultValue, &isPK); err != nil { + return + } + + if fieldGlob != "" { + if matched, _ := filepath.Match(fieldGlob, columnName); !matched { + continue + } + } + + // process flag + colFlag := uint16(0) + + if isNotNull { + colFlag |= my.NOT_NULL_FLAG + } + if isPK { + colFlag |= my.NOT_NULL_FLAG + colFlag |= my.PRI_KEY_FLAG + } + + fields = append(fields, &my.Field{ + Name: []byte(columnName), + OrgName: []byte(columnName), + Table: []byte(table), + OrgTable: []byte(table), + Schema: []byte(c.curDB), + Flag: colFlag, + Charset: uint16(my.DEFAULT_COLLATION_ID), + ColumnLength: 0, // no column length specified + Type: c.detectColumnType(typeString), + }) + } + + return +} + +// HandleStmtPrepare handle COM_STMT_PREPARE, params is the param number for this statement, columns is the column number +// context will be used later for statement execute. +func (c *Cursor) HandleStmtPrepare(query string) (params int, columns int, context interface{}, err error) { + // TODO(xq26144), not implemented + // According to the libmysql standard: https://github.com/mysql/mysql-server/blob/8.0/libmysql/libmysql.cc#L1599 + // the COM_STMT_PREPARE should return the correct bind parameter count (which can be implemented by newly created parser) + // and should return the correct number of return fields (which can not be implemented right now with new query plan logic embedded) + + err = my.NewError(my.ER_NOT_SUPPORTED_YET, "stmt prepare is not supported yet") + return +} + +// HandleStmtExecute handle COM_STMT_EXECUTE, context is the previous one set in prepare +// query is the statement prepare query, and args is the params for this statement. +func (c *Cursor) HandleStmtExecute(context interface{}, query string, args []interface{}) (result *my.Result, err error) { + // same to COM_STMT_PREPARE + err = my.NewError(my.ER_NOT_SUPPORTED_YET, "stmt execute is not supported yet") + return +} + +// HandleStmtClose handle COM_STMT_CLOSE, context is the previous one set in prepare +// this handler has no response. +func (c *Cursor) HandleStmtClose(context interface{}) (err error) { + return +} + +// HandleOtherCommand handle any other command that is not currently handled by the library, +// default implementation for this method will return an ER_UNKNOWN_ERROR. +func (c *Cursor) HandleOtherCommand(cmd byte, data []byte) (err error) { + return my.NewError(my.ER_UNKNOWN_ERROR, fmt.Sprintf("command %d is not supported now", cmd)) +} + +// SetUser set back current user. +func (c *Cursor) SetUser(user string) { + c.curUser = user +} diff --git a/cmd/cql-secure-gateway/errors.go b/cmd/cql-secure-gateway/errors.go new file mode 100644 index 000000000..158414ad8 --- /dev/null +++ b/cmd/cql-secure-gateway/errors.go @@ -0,0 +1,31 @@ +/* + * Copyright 2018 The CovenantSQL Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package main + +import ( + cw "github.com/CovenantSQL/CovenantSQL/cmd/cql-secure-gateway/casbin" + "github.com/pkg/errors" +) + +var ( + // ErrInvalidConfig defines error for invalid SG config. + ErrInvalidConfig = errors.New("invalid config") + // ErrFieldEncryption defines invalid field encryption config (usually assigned multiple encryption keys to single column). + ErrFieldEncryption = errors.New("invalid field encryption config") + // ErrInvalidField defines invalid field format. + ErrInvalidField = cw.ErrInvalidField +) diff --git a/cmd/cql-secure-gateway/main.go b/cmd/cql-secure-gateway/main.go new file mode 100644 index 000000000..490fd26c3 --- /dev/null +++ b/cmd/cql-secure-gateway/main.go @@ -0,0 +1,93 @@ +/* + * Copyright 2018 The CovenantSQL Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package main + +import ( + "flag" + "fmt" + "os" + "os/signal" + "runtime" + + "github.com/CovenantSQL/CovenantSQL/client" + "github.com/CovenantSQL/CovenantSQL/crypto/asymmetric" + "github.com/CovenantSQL/CovenantSQL/utils/log" + "golang.org/x/sys/unix" +) + +const name = "cql-secure-gateway" + +var ( + version = "unknown" + configFile string + password string + showVersion bool +) + +func init() { + flag.StringVar(&configFile, "config", "./config.yaml", "config file for mysql adapter") + flag.StringVar(&password, "password", "", "master key password") + flag.BoolVar(&asymmetric.BypassSignature, "bypassSignature", false, + "Disable signature sign and verify, for testing") + flag.BoolVar(&showVersion, "version", false, "Show version information and exit") +} + +func main() { + flag.Parse() + if showVersion { + fmt.Printf("%v %v %v %v %v\n", + name, version, runtime.GOOS, runtime.GOARCH, runtime.Version()) + os.Exit(0) + } + + flag.Visit(func(f *flag.Flag) { + log.Infof("Args %#v : %s", f.Name, f.Value) + }) + + // init client + var err error + if err = client.Init(configFile, []byte(password)); err != nil { + log.WithError(err).Fatal("init covenantsql client failed") + return + } + + // load config + var sc *sgConfig + if sc, err = loadConfig(configFile); err != nil { + log.WithError(err).Fatal("load secure gateway config failed") + return + } + + stop := make(chan os.Signal, 1) + signal.Notify(stop, os.Interrupt, unix.SIGTERM) + + server, err := NewServer(sc) + if err != nil { + log.WithError(err).Fatal("init server failed") + return + } + + go server.Serve() + + log.Info("start mysql adapter") + + <-stop + + server.Shutdown() + + log.Info("stopped mysql adapter") +} diff --git a/cmd/cql-secure-gateway/scanner.go b/cmd/cql-secure-gateway/scanner.go new file mode 100644 index 000000000..fb96168c2 --- /dev/null +++ b/cmd/cql-secure-gateway/scanner.go @@ -0,0 +1,106 @@ +/* + * Copyright 2018 The CovenantSQL Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package main + +import ( + "database/sql" + "io" + "time" +) + +type rowScanner struct { + fieldCnt int + column int // current column + fields []interface{} // temp fields + scanArgs []interface{} +} + +func newRowScanner(fieldCnt int) (s *rowScanner) { + s = &rowScanner{ + fieldCnt: fieldCnt, + column: 0, + fields: make([]interface{}, fieldCnt), + scanArgs: make([]interface{}, fieldCnt), + } + + for i := 0; i != fieldCnt; i++ { + s.scanArgs[i] = s + } + + return +} + +func (s *rowScanner) Scan(src interface{}) error { + if s.fieldCnt <= s.column { + // read complete + return io.EOF + } + + // type conversions + switch srcValue := src.(type) { + case []byte: + s.fields[s.column] = string(srcValue) + case bool: + if srcValue { + s.fields[s.column] = int8(1) + } else { + s.fields[s.column] = int8(0) + } + case time.Time: + s.fields[s.column] = srcValue.String() + default: + s.fields[s.column] = src + } + + s.column++ + + return nil +} + +func (s *rowScanner) GetRow() []interface{} { + return s.fields +} + +func (s *rowScanner) ScanArgs() []interface{} { + // reset + s.column = 0 + s.fields = make([]interface{}, s.fieldCnt) + return s.scanArgs +} + +func readAllRows(rows *sql.Rows) (result [][]interface{}, err error) { + var columns []string + if columns, err = rows.Columns(); err != nil { + return + } + + rs := newRowScanner(len(columns)) + result = make([][]interface{}, 0) + + for rows.Next() { + err = rows.Scan(rs.ScanArgs()...) + if err != nil { + return + } + + result = append(result, rs.GetRow()) + } + + err = rows.Err() + + return +} diff --git a/cmd/cql-secure-gateway/server.go b/cmd/cql-secure-gateway/server.go new file mode 100644 index 000000000..b64ec1f65 --- /dev/null +++ b/cmd/cql-secure-gateway/server.go @@ -0,0 +1,80 @@ +/* + * Copyright 2018 The CovenantSQL Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package main + +import ( + "net" + + "github.com/CovenantSQL/CovenantSQL/utils/log" + mys "github.com/siddontang/go-mysql/server" +) + +// Server defines the main logic of mysql protocol adapter. +type Server struct { + listener net.Listener + cfg *sgConfig +} + +// NewServer bind the service port and return a runnable adapter. +func NewServer(sc *sgConfig) (s *Server, err error) { + s = &Server{ + cfg: sc, + } + + if s.listener, err = net.Listen("tcp", sc.ListenAddr); err != nil { + return + } + + return +} + +// Serve starts the server. +func (s *Server) Serve() { + for { + conn, err := s.listener.Accept() + if err != nil { + return + } + + go s.handleConn(conn) + } +} + +func (s *Server) handleConn(conn net.Conn) { + cur := NewCursor(s) + h, err := mys.NewConnWithUsers(conn, s.cfg.AuthConfig.Users, cur) + + if err != nil { + log.WithError(err).Error("process connection failed") + return + } + + // set connection user to cursor + cur.SetUser(h.GetUser()) + + for { + err = h.HandleCommand() + if err != nil { + return + } + } +} + +// Shutdown ends the server. +func (s *Server) Shutdown() { + s.listener.Close() +} diff --git a/cmd/cql-secure-gateway/utils.go b/cmd/cql-secure-gateway/utils.go new file mode 100644 index 000000000..e64d05980 --- /dev/null +++ b/cmd/cql-secure-gateway/utils.go @@ -0,0 +1,58 @@ +/* + * Copyright 2018 The CovenantSQL Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package main + +import ( + "path/filepath" + "strings" + + "github.com/pkg/errors" +) + +func fieldMatches(f1 string, f2 string) (matched bool, err error) { + // split field to parts + parts1 := strings.Split(f1, ".") + parts2 := strings.Split(f2, ".") + + if len(parts1) != 3 { + err = errors.Wrapf(ErrInvalidField, "field %s is invalid", f1) + return + } + if len(parts2) != 3 { + err = errors.Wrapf(ErrInvalidField, "field %s is invalid", f2) + return + } + + for i := 0; i != 3; i++ { + var m1, m2 bool + if m1, err = filepath.Match(parts1[i], parts2[i]); err != nil { + err = errors.Wrapf(err, "match field part %s and %s failed", parts1[i], parts2[i]) + return + } + if m2, err = filepath.Match(parts2[i], parts1[i]); err != nil { + err = errors.Wrapf(err, "match field part %s and %s failed", parts1[i], parts2[i]) + return + } + if !m1 && !m2 { + // not matched + return + } + } + + matched = true + return +} From 5320fc7916987a7da8639456374533c05dc0c29a Mon Sep 17 00:00:00 2001 From: Qi Xiao Date: Mon, 19 Nov 2018 18:33:41 +0800 Subject: [PATCH 03/24] Add config file --- cmd/cql-secure-gateway/config.go | 223 +++++++++++++++++++++ cmd/cql-secure-gateway/config_test.go | 271 ++++++++++++++++++++++++++ 2 files changed, 494 insertions(+) create mode 100644 cmd/cql-secure-gateway/config.go create mode 100644 cmd/cql-secure-gateway/config_test.go diff --git a/cmd/cql-secure-gateway/config.go b/cmd/cql-secure-gateway/config.go new file mode 100644 index 000000000..3b90e4f30 --- /dev/null +++ b/cmd/cql-secure-gateway/config.go @@ -0,0 +1,223 @@ +/* + * Copyright 2018 The CovenantSQL Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package main + +import ( + "io/ioutil" + "os" + "path" + "strings" + + cw "github.com/CovenantSQL/CovenantSQL/cmd/cql-secure-gateway/casbin" + "github.com/CovenantSQL/CovenantSQL/conf" + "github.com/CovenantSQL/CovenantSQL/crypto/asymmetric" + "github.com/CovenantSQL/CovenantSQL/crypto/kms" + "github.com/CovenantSQL/CovenantSQL/utils/log" + "github.com/casbin/casbin" + "github.com/pkg/errors" + "gopkg.in/yaml.v2" +) + +type encryptionConfig struct { + KeyPath string `yaml:"Key"` // key path for encryption config + Key *asymmetric.PrivateKey `yaml:"-"` // encryption key + Fields []string `yaml:"Fields"` // encrypted fields config, support field group defines in grants config +} + +type authConfig struct { + Users map[string]string `yaml:"Users"` // user to password mapping + Grants *cw.Config `yaml:"Grants"` // casbin grants config + Enforcer *casbin.Enforcer `yaml:"-"` // casbin enforcer + Encryption []*encryptionConfig `yaml:"Encryption"` // encryption fields settings + FlattenEncryptionConfig map[string]int `yaml:"-"` // field to key offset +} + +type sgConfig struct { + ListenAddr string `yaml:"ListenAddr"` // mysql compatible rpc listen address + AuthConfig *authConfig `yaml:"Auth"` // authentication related config +} + +type configWrapper struct { + Config *sgConfig `yaml:"SecureGateway"` // secure gateway config +} + +func loadConfig(configFile string) (cfg *sgConfig, err error) { + var ( + configBytes []byte + wrapper *configWrapper + ) + if configBytes, err = ioutil.ReadFile(configFile); err != nil { + log.WithError(err).Error("failed to read config file") + return + } + if err = yaml.Unmarshal(configBytes, &wrapper); err != nil { + log.WithError(err).Error("failed to parse config file") + return + } + + cfg = wrapper.Config + err = cfg.buildAndValidate() + + return +} + +func (sc *sgConfig) buildAndValidate() (err error) { + if sc == nil { + err = errors.Wrap(ErrInvalidConfig, "nil secure gateway config") + return + } + if sc.ListenAddr == "" { + err = errors.Wrap(ErrInvalidConfig, "empty listen addr") + log.WithError(err).Error("invalid config") + return + } + if sc.AuthConfig == nil { + err = errors.Wrap(ErrInvalidConfig, "nil auth config") + log.WithError(err).Error("invalid config") + return + } + if err = sc.AuthConfig.buildEnforcer(); err != nil { + log.WithError(err).Error("failed to load grants") + return + } + if err = sc.AuthConfig.buildEncryption(); err != nil { + log.WithError(err).Error("failed to load encryption config") + return + } + + return +} + +func (ac *authConfig) buildEnforcer() (err error) { + ac.Enforcer, err = cw.NewCasbin(ac.Grants) + return +} + +func (ac *authConfig) buildEncryption() (err error) { + var workingRootDir string + + if conf.GConf != nil && conf.GConf.WorkingRoot != "" { + workingRootDir = conf.GConf.WorkingRoot + } else { + if workingRootDir, err = os.Getwd(); err != nil { + return + } + } + + ac.FlattenEncryptionConfig = make(map[string]int) + + // load and validate encryption config to match with grants + for i, ec := range ac.Encryption { + if err = ac.buildSingleEncryptionConfig(workingRootDir, i, ec); err != nil { + // return + break + } + } + + return +} + +func (ac *authConfig) buildSingleEncryptionConfig(wd string, configIndex int, ec *encryptionConfig) (err error) { + // validate encryption keys existence + if !path.IsAbs(ec.KeyPath) { + ec.KeyPath = path.Join(wd, ec.KeyPath) + } + + // load key, does not support password empowered private key + if ec.Key, err = kms.LoadPrivateKey(ec.KeyPath, []byte{}); err != nil { + err = errors.Wrapf(err, "load key %s failed", ec.KeyPath) + return + } + + // validate fields + for _, f := range ec.Fields { + if ac.Grants != nil && ac.Grants.FieldGroup != nil { + if _, exists := ac.Grants.FieldGroup[f]; exists { + // exists in field group + // flatten + + for _, sf := range ac.Grants.FieldGroup[f] { + if err = ac.setFieldEncryptionKey(sf, configIndex); err != nil { + return + } + } + + continue + } + } + + // validate as normal .
. field + if err = ac.validateField(f); err != nil { + return + } + + // set to flatten mapping + if err = ac.setFieldEncryptionKey(f, configIndex); err != nil { + return + } + } + + return +} + +func (ac *authConfig) setFieldEncryptionKey(f string, ecOffset int) (err error) { + // check field encryption configuration over-lapping + // iterate existing setting + + for p, o := range ac.FlattenEncryptionConfig { + var m bool + if m, err = fieldMatches(p, f); err != nil { + return + } + + if m && o != ecOffset { + err = errors.Wrapf(ErrFieldEncryption, + "duplicated encryption assigned to column (previous: %s -> %s, current: %s -> %s)", + p, ac.Encryption[o].KeyPath, + f, ac.Encryption[ecOffset].KeyPath, + ) + return + } + } + + // set new record + ac.FlattenEncryptionConfig[f] = ecOffset + + return +} + +func (ac *authConfig) validateField(f string) (err error) { + parts := strings.Split(f, ".") + + if len(parts) != 3 { + err = errors.Wrapf(ErrInvalidField, + "invalid field: %s, should be in form \".
.\", wildcard is supported", f) + return + } + + for _, p := range parts { + p = strings.TrimSpace(p) + + if p == "" { + err = errors.Wrapf(ErrInvalidField, + "invalid field: %s, /
/ parts should not be blanked", f) + return + } + } + + return +} diff --git a/cmd/cql-secure-gateway/config_test.go b/cmd/cql-secure-gateway/config_test.go new file mode 100644 index 000000000..b99312550 --- /dev/null +++ b/cmd/cql-secure-gateway/config_test.go @@ -0,0 +1,271 @@ +/* + * Copyright 2018 The CovenantSQL Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package main + +import ( + "io/ioutil" + "os" + "path/filepath" + "testing" + + "github.com/CovenantSQL/CovenantSQL/conf" + "github.com/CovenantSQL/CovenantSQL/crypto/asymmetric" + "github.com/CovenantSQL/CovenantSQL/crypto/kms" + "github.com/CovenantSQL/CovenantSQL/utils/log" + "github.com/pkg/errors" + + . "github.com/smartystreets/goconvey/convey" +) + +const ( + validConfig = ` +SecureGateway: + ListenAddr: 127.0.0.1:4665 + Auth: + Users: + user1: yD7LCvhH + user2: AUZzFq4X + user3: jhAWQTnh + Grants: + Policies: + - User: user1 + Field: db1.tbl1.col1 + Action: read + - User: admin_group + Field: db1.tbl1.col1 + Action: write + - User: read_group + Field: tbl2_fields + Action: read + - User: admin_group + Field: db1.tbl3.col* + Action: write + UserGroups: + admin_group: + - user2 + - user3 + read_group: + - user1 + - user2 + - user3 + FieldGroups: + tbl2_fields: + - db1.tbl2.col* + Encryption: + - Key: key1.key + Fields: + - tbl2_fields + - Key: key2.key + Fields: + - db1.tbl3.col* +` + withoutListenAddrConfig = ` +SecureGateway: + Auth: + Users: + user1: yD7LCvhH + user2: AUZzFq4X + user3: jhAWQTnh + Grants: + Policies: + - User: user1 + Field: db1.tbl1.col1 + Action: read + - User: admin_group + Field: db1.tbl1.col1 + Action: write + - User: read_group + Field: tbl2_fields + Action: read + - User: admin_group + Field: db1.tbl3.col* + Action: write + UserGroups: + admin_group: + - user2 + - user3 + read_group: + - user1 + - user2 + - user3 + FieldGroups: + tbl2_fields: + - db1.tbl2.col* + Encryption: + - Key: key1.key + Fields: + - tbl2_fields + - Key: key2.key + Fields: + - db1.tbl3.col* +` + withoutAuthConfig = ` +SecureGateway: + ListenAddr: 127.0.0.1:4665 +` + invalidGrantsConfig = ` +SecureGateway: + Auth: + Users: + user1: yD7LCvhH + user2: AUZzFq4X + user3: jhAWQTnh + Grants: + Policies: + - User: user1 + Field: "invalid field" + Action: read + - User: admin_group + Field: db1.tbl1.col1 + Action: write + - User: read_group + Field: tbl2_fields + Action: read + - User: admin_group + Field: db1.tbl3.col* + Action: write + UserGroups: + admin_group: + - user2 + - user3 + read_group: + - user1 + - user2 + - user3 + FieldGroups: + tbl2_fields: + - db1.tbl2.col* + Encryption: + - Key: key1.key + Fields: + - tbl2_fields + - Key: key2.key + Fields: + - db1.tbl3.col* +` + overlappedFieldsEncryptionConfig = ` +SecureGateway: + ListenAddr: 127.0.0.1:4665 + Auth: + Users: + user1: yD7LCvhH + user2: AUZzFq4X + user3: jhAWQTnh + Grants: + Policies: + - User: user1 + Field: db1.tbl1.col1 + Action: read + - User: admin_group + Field: db1.tbl1.col1 + Action: write + - User: read_group + Field: tbl2_fields + Action: read + - User: admin_group + Field: db1.tbl3.col* + Action: write + UserGroups: + admin_group: + - user2 + - user3 + read_group: + - user1 + - user2 + - user3 + FieldGroups: + tbl2_fields: + - db1.tbl2.col* + Encryption: + - Key: key1.key + Fields: + - tbl2_fields + - db1.tbl3.col1 + - Key: key2.key + Fields: + - db1.tbl3.col* +` +) + +func generateKey(path string) (err error) { + var privKey *asymmetric.PrivateKey + if privKey, _, err = asymmetric.GenSecp256k1KeyPair(); err != nil { + return + } + err = kms.SavePrivateKey(path, privKey, []byte{}) + return +} + +func TestLoadConfig(t *testing.T) { + log.SetLevel(log.FatalLevel) + Convey("test load config", t, func() { + // set global config + var dirName string + var err error + dirName, err = ioutil.TempDir("", "sg_test") + So(err, ShouldBeNil) + defer os.RemoveAll(dirName) + conf.GConf = &conf.Config{ + WorkingRoot: dirName, + } + withDirPrefix := func(p string) string { + return filepath.Join(dirName, p) + } + // load non-exist config file + _, err = loadConfig("non-exists") + So(err, ShouldNotBeNil) + // load invalid yaml config + err = ioutil.WriteFile(withDirPrefix("invalid.yaml"), []byte("haha"), 0600) + So(err, ShouldBeNil) + _, err = loadConfig(withDirPrefix("invalid.yaml")) + So(err, ShouldNotBeNil) + // generate key for control group + err = generateKey(withDirPrefix("key1.key")) + So(err, ShouldBeNil) + err = generateKey(withDirPrefix("key2.key")) + So(err, ShouldBeNil) + // test a correct control group + err = ioutil.WriteFile(withDirPrefix("valid.yaml"), []byte(validConfig), 0600) + So(err, ShouldBeNil) + _, err = loadConfig(withDirPrefix("valid.yaml")) + So(err, ShouldBeNil) + // test invalid config without listen addr + err = ioutil.WriteFile(withDirPrefix("without_listen_addr.yaml"), []byte(withoutListenAddrConfig), 0600) + So(err, ShouldBeNil) + _, err = loadConfig(withDirPrefix("without_listen_addr.yaml")) + So(err, ShouldNotBeNil) + So(errors.Cause(err), ShouldEqual, ErrInvalidConfig) + // test invalid config without authentication section + err = ioutil.WriteFile(withDirPrefix("without_auth.yaml"), []byte(withoutAuthConfig), 0600) + So(err, ShouldBeNil) + _, err = loadConfig(withDirPrefix("without_auth.yaml")) + So(err, ShouldNotBeNil) + So(errors.Cause(err), ShouldEqual, ErrInvalidConfig) + // test invalid grants settings (casbin enforcer config) + err = ioutil.WriteFile(withDirPrefix("invalid_grants.yaml"), []byte(invalidGrantsConfig), 0600) + So(err, ShouldBeNil) + _, err = loadConfig(withDirPrefix("invalid_grants.yaml")) + So(err, ShouldNotBeNil) + // test invalid encryption config + err = ioutil.WriteFile(withDirPrefix("overlapped_fields_encryption.yaml"), + []byte(overlappedFieldsEncryptionConfig), 0600) + So(err, ShouldBeNil) + _, err = loadConfig(withDirPrefix("overlapped_fields_encryption.yaml")) + So(err, ShouldNotBeNil) + So(errors.Cause(err), ShouldEqual, ErrFieldEncryption) + }) +} From 98c4bdd00f12f8aaf2b2e0594389d0470df298b7 Mon Sep 17 00:00:00 2001 From: Qi Xiao Date: Tue, 20 Nov 2018 10:00:09 +0800 Subject: [PATCH 04/24] More verbose test on config parsing --- cmd/cql-secure-gateway/config_test.go | 237 +++++++++++++++++++++++++- 1 file changed, 235 insertions(+), 2 deletions(-) diff --git a/cmd/cql-secure-gateway/config_test.go b/cmd/cql-secure-gateway/config_test.go index b99312550..26a88638f 100644 --- a/cmd/cql-secure-gateway/config_test.go +++ b/cmd/cql-secure-gateway/config_test.go @@ -27,7 +27,6 @@ import ( "github.com/CovenantSQL/CovenantSQL/crypto/kms" "github.com/CovenantSQL/CovenantSQL/utils/log" "github.com/pkg/errors" - . "github.com/smartystreets/goconvey/convey" ) @@ -72,6 +71,9 @@ SecureGateway: - Key: key2.key Fields: - db1.tbl3.col* +` + nilConfig = ` +SecureGateway: null ` withoutListenAddrConfig = ` SecureGateway: @@ -119,6 +121,7 @@ SecureGateway: ` invalidGrantsConfig = ` SecureGateway: + ListenAddr: 127.0.0.1:4665 Auth: Users: user1: yD7LCvhH @@ -198,6 +201,175 @@ SecureGateway: - Key: key2.key Fields: - db1.tbl3.col* +` + anotherOverlappedFieldsEncryptionConfig = ` +SecureGateway: + ListenAddr: 127.0.0.1:4665 + Auth: + Users: + user1: yD7LCvhH + user2: AUZzFq4X + user3: jhAWQTnh + Grants: + Policies: + - User: user1 + Field: db1.tbl1.col1 + Action: read + - User: admin_group + Field: db1.tbl1.col1 + Action: write + - User: read_group + Field: tbl2_fields + Action: read + - User: admin_group + Field: db1.tbl3.col* + Action: write + UserGroups: + admin_group: + - user2 + - user3 + read_group: + - user1 + - user2 + - user3 + FieldGroups: + tbl2_fields: + - db1.tbl2.col* + Encryption: + - Key: key2.key + Fields: + - db1.tbl2.col2 + - Key: key1.key + Fields: + - tbl2_fields +` + invalidEncryptionField = ` +SecureGateway: + ListenAddr: 127.0.0.1:4665 + Auth: + Users: + user1: yD7LCvhH + user2: AUZzFq4X + user3: jhAWQTnh + Grants: + Policies: + - User: user1 + Field: db1.tbl1.col1 + Action: read + - User: admin_group + Field: db1.tbl1.col1 + Action: write + - User: read_group + Field: tbl2_fields + Action: read + - User: admin_group + Field: db1.tbl3.col* + Action: write + UserGroups: + admin_group: + - user2 + - user3 + read_group: + - user1 + - user2 + - user3 + FieldGroups: + tbl2_fields: + - db1.tbl2.col* + Encryption: + - Key: key1.key + Fields: + - tbl2_fields + - Key: key2.key + Fields: + - db1.tbl3.col* + - invalid_field +` + anotherInvalidEncryptionField = ` +SecureGateway: + ListenAddr: 127.0.0.1:4665 + Auth: + Users: + user1: yD7LCvhH + user2: AUZzFq4X + user3: jhAWQTnh + Grants: + Policies: + - User: user1 + Field: db1.tbl1.col1 + Action: read + - User: admin_group + Field: db1.tbl1.col1 + Action: write + - User: read_group + Field: tbl2_fields + Action: read + - User: admin_group + Field: db1.tbl3.col* + Action: write + UserGroups: + admin_group: + - user2 + - user3 + read_group: + - user1 + - user2 + - user3 + FieldGroups: + tbl2_fields: + - db1.tbl2.col* + Encryption: + - Key: key1.key + Fields: + - tbl2_fields + - Key: key2.key + Fields: + - db1.tbl3.col* + - db1..col1 +` + nonExistsKeyConfig = ` +SecureGateway: + ListenAddr: 127.0.0.1:4665 + Auth: + Users: + user1: yD7LCvhH + user2: AUZzFq4X + user3: jhAWQTnh + Grants: + Policies: + - User: user1 + Field: db1.tbl1.col1 + Action: read + - User: admin_group + Field: db1.tbl1.col1 + Action: write + - User: read_group + Field: tbl2_fields + Action: read + - User: admin_group + Field: db1.tbl3.col* + Action: write + UserGroups: + admin_group: + - user2 + - user3 + read_group: + - user1 + - user2 + - user3 + FieldGroups: + tbl2_fields: + - db1.tbl2.col* + Encryption: + - Key: key1.key + Fields: + - tbl2_fields + - Key: key2.key + Fields: + - db1.tbl3.col* + - Key: key3.key + Fields: + - db1.tbl4.col1 ` ) @@ -225,47 +397,108 @@ func TestLoadConfig(t *testing.T) { withDirPrefix := func(p string) string { return filepath.Join(dirName, p) } + // load non-exist config file _, err = loadConfig("non-exists") So(err, ShouldNotBeNil) + // load invalid yaml config err = ioutil.WriteFile(withDirPrefix("invalid.yaml"), []byte("haha"), 0600) So(err, ShouldBeNil) _, err = loadConfig(withDirPrefix("invalid.yaml")) So(err, ShouldNotBeNil) + // generate key for control group err = generateKey(withDirPrefix("key1.key")) So(err, ShouldBeNil) err = generateKey(withDirPrefix("key2.key")) So(err, ShouldBeNil) + // test a correct control group err = ioutil.WriteFile(withDirPrefix("valid.yaml"), []byte(validConfig), 0600) So(err, ShouldBeNil) _, err = loadConfig(withDirPrefix("valid.yaml")) So(err, ShouldBeNil) + + // test without valid global config + var ( + tempConf *conf.Config + tempDir string + ) + tempConf = conf.GConf + conf.GConf = nil + tempDir, err = os.Getwd() + So(err, ShouldBeNil) + os.Chdir(dirName) + _, err = loadConfig(withDirPrefix("valid.yaml")) + So(err, ShouldBeNil) + conf.GConf = tempConf + os.Chdir(tempDir) + + // test nil config + err = ioutil.WriteFile(withDirPrefix("nil.yaml"), []byte(nilConfig), 0600) + So(err, ShouldBeNil) + _, err = loadConfig(withDirPrefix("nil.yaml")) + So(err, ShouldNotBeNil) + So(errors.Cause(err), ShouldEqual, ErrInvalidConfig) + // test invalid config without listen addr err = ioutil.WriteFile(withDirPrefix("without_listen_addr.yaml"), []byte(withoutListenAddrConfig), 0600) So(err, ShouldBeNil) _, err = loadConfig(withDirPrefix("without_listen_addr.yaml")) So(err, ShouldNotBeNil) So(errors.Cause(err), ShouldEqual, ErrInvalidConfig) + // test invalid config without authentication section err = ioutil.WriteFile(withDirPrefix("without_auth.yaml"), []byte(withoutAuthConfig), 0600) So(err, ShouldBeNil) _, err = loadConfig(withDirPrefix("without_auth.yaml")) So(err, ShouldNotBeNil) So(errors.Cause(err), ShouldEqual, ErrInvalidConfig) + // test invalid grants settings (casbin enforcer config) err = ioutil.WriteFile(withDirPrefix("invalid_grants.yaml"), []byte(invalidGrantsConfig), 0600) So(err, ShouldBeNil) _, err = loadConfig(withDirPrefix("invalid_grants.yaml")) So(err, ShouldNotBeNil) - // test invalid encryption config + + // test overlapped encryption fields config err = ioutil.WriteFile(withDirPrefix("overlapped_fields_encryption.yaml"), []byte(overlappedFieldsEncryptionConfig), 0600) So(err, ShouldBeNil) _, err = loadConfig(withDirPrefix("overlapped_fields_encryption.yaml")) So(err, ShouldNotBeNil) So(errors.Cause(err), ShouldEqual, ErrFieldEncryption) + + // test another overlapped encryption fields config + err = ioutil.WriteFile(withDirPrefix("another_overlapped_fields_encryption.yaml"), + []byte(anotherOverlappedFieldsEncryptionConfig), 0600) + So(err, ShouldBeNil) + _, err = loadConfig(withDirPrefix("another_overlapped_fields_encryption.yaml")) + So(err, ShouldNotBeNil) + So(errors.Cause(err), ShouldEqual, ErrFieldEncryption) + + // test invalid encryption field config + err = ioutil.WriteFile(withDirPrefix("invalid_encryption_field.yaml"), + []byte(invalidEncryptionField), 0600) + So(err, ShouldBeNil) + _, err = loadConfig(withDirPrefix("invalid_encryption_field.yaml")) + So(err, ShouldNotBeNil) + So(errors.Cause(err), ShouldEqual, ErrInvalidField) + + // test invalid encryption field config + err = ioutil.WriteFile(withDirPrefix("another_invalid_encryption_field.yaml"), + []byte(anotherInvalidEncryptionField), 0600) + So(err, ShouldBeNil) + _, err = loadConfig(withDirPrefix("another_invalid_encryption_field.yaml")) + So(err, ShouldNotBeNil) + So(errors.Cause(err), ShouldEqual, ErrInvalidField) + + // test non-existent key + err = ioutil.WriteFile(withDirPrefix("non_exists_key.yaml"), + []byte(nonExistsKeyConfig), 0600) + So(err, ShouldBeNil) + _, err = loadConfig(withDirPrefix("non_exists_key.yaml")) + So(err, ShouldNotBeNil) }) } From 9193076b61ee6a5ac2252d91b54b7b1fe17d09c5 Mon Sep 17 00:00:00 2001 From: Qi Xiao Date: Tue, 20 Nov 2018 12:36:03 +0800 Subject: [PATCH 05/24] Update deps Update sqlparser to support explain statement --- Gopkg.lock | 40 +- .../github.com/CovenantSQL/sqlparser/ast.go | 34 +- .../github.com/CovenantSQL/sqlparser/sql.go | 2353 +++++++++-------- vendor/github.com/CovenantSQL/sqlparser/sql.y | 12 +- .../github.com/CovenantSQL/sqlparser/token.go | 1 + .../github.com/Knetic/govaluate/CONTRIBUTORS | 12 + .../Knetic/govaluate/EvaluableExpression.go | 272 ++ .../govaluate/EvaluableExpression_sql.go | 167 ++ .../Knetic/govaluate/ExpressionToken.go | 9 + vendor/github.com/Knetic/govaluate/LICENSE | 21 + vendor/github.com/Knetic/govaluate/MANUAL.md | 176 ++ .../Knetic/govaluate/OperatorSymbol.go | 306 +++ vendor/github.com/Knetic/govaluate/README.md | 210 ++ .../github.com/Knetic/govaluate/TokenKind.go | 72 + .../Knetic/govaluate/evaluationStage.go | 359 +++ .../Knetic/govaluate/expressionFunctions.go | 8 + .../govaluate/expressionOutputStream.go | 46 + .../github.com/Knetic/govaluate/lexerState.go | 350 +++ .../Knetic/govaluate/lexerStream.go | 39 + .../github.com/Knetic/govaluate/parameters.go | 32 + vendor/github.com/Knetic/govaluate/parsing.go | 450 ++++ .../Knetic/govaluate/sanitizedParameters.go | 71 + .../Knetic/govaluate/stagePlanner.go | 675 +++++ vendor/github.com/Knetic/govaluate/test.sh | 32 + .../Knetic/govaluate/tokenStream.go | 36 + vendor/github.com/casbin/casbin/LICENSE | 201 ++ vendor/github.com/casbin/casbin/README.md | 372 +++ .../github.com/casbin/casbin/casbin-logo.png | Bin 0 -> 34267 bytes .../casbin/casbin/effect/default_effector.go | 75 + .../casbin/casbin/effect/effector.go | 31 + vendor/github.com/casbin/casbin/enforcer.go | 428 +++ .../casbin/casbin/enforcer_cached.go | 74 + .../github.com/casbin/casbin/enforcer_safe.go | 102 + .../casbin/casbin/enforcer_synced.go | 223 ++ .../github.com/casbin/casbin/internal_api.go | 85 + .../casbin/casbin/log/default_logger.go | 42 + .../github.com/casbin/casbin/log/log_util.go | 37 + vendor/github.com/casbin/casbin/log/logger.go | 30 + .../casbin/casbin/management_api.go | 265 ++ .../casbin/casbin/model/assertion.go | 60 + .../casbin/casbin/model/function.go | 40 + .../github.com/casbin/casbin/model/model.go | 130 + .../github.com/casbin/casbin/model/policy.go | 146 + .../casbin/casbin/persist/adapter.go | 56 + .../casbin/casbin/persist/adapter_filtered.go | 29 + .../casbin/persist/file-adapter/adapter.go | 117 + .../persist/file-adapter/adapter_filtered.go | 137 + .../persist/file-adapter/adapter_mock.go | 100 + .../casbin/casbin/persist/watcher.go | 27 + .../rbac/default-role-manager/role_manager.go | 257 ++ .../casbin/casbin/rbac/role_manager.go | 38 + vendor/github.com/casbin/casbin/rbac_api.go | 184 ++ .../casbin/casbin/rbac_api_synced.go | 121 + .../casbin/casbin/rbac_api_with_domains.go | 38 + .../casbin/casbin/util/builtin_operators.go | 160 ++ vendor/github.com/casbin/casbin/util/util.go | 105 + vendor/golang.org/x/sys/unix/mkerrors.sh | 2 + .../x/sys/unix/zerrors_linux_386.go | 34 + .../x/sys/unix/zerrors_linux_amd64.go | 34 + .../x/sys/unix/zerrors_linux_arm.go | 34 + .../x/sys/unix/zerrors_linux_arm64.go | 34 + .../x/sys/unix/zerrors_linux_mips.go | 34 + .../x/sys/unix/zerrors_linux_mips64.go | 34 + .../x/sys/unix/zerrors_linux_mips64le.go | 34 + .../x/sys/unix/zerrors_linux_mipsle.go | 34 + .../x/sys/unix/zerrors_linux_ppc64.go | 34 + .../x/sys/unix/zerrors_linux_ppc64le.go | 34 + .../x/sys/unix/zerrors_linux_riscv64.go | 34 + .../x/sys/unix/zerrors_linux_s390x.go | 34 + 69 files changed, 8723 insertions(+), 1180 deletions(-) create mode 100644 vendor/github.com/Knetic/govaluate/CONTRIBUTORS create mode 100644 vendor/github.com/Knetic/govaluate/EvaluableExpression.go create mode 100644 vendor/github.com/Knetic/govaluate/EvaluableExpression_sql.go create mode 100644 vendor/github.com/Knetic/govaluate/ExpressionToken.go create mode 100644 vendor/github.com/Knetic/govaluate/LICENSE create mode 100644 vendor/github.com/Knetic/govaluate/MANUAL.md create mode 100644 vendor/github.com/Knetic/govaluate/OperatorSymbol.go create mode 100644 vendor/github.com/Knetic/govaluate/README.md create mode 100644 vendor/github.com/Knetic/govaluate/TokenKind.go create mode 100644 vendor/github.com/Knetic/govaluate/evaluationStage.go create mode 100644 vendor/github.com/Knetic/govaluate/expressionFunctions.go create mode 100644 vendor/github.com/Knetic/govaluate/expressionOutputStream.go create mode 100644 vendor/github.com/Knetic/govaluate/lexerState.go create mode 100644 vendor/github.com/Knetic/govaluate/lexerStream.go create mode 100644 vendor/github.com/Knetic/govaluate/parameters.go create mode 100644 vendor/github.com/Knetic/govaluate/parsing.go create mode 100644 vendor/github.com/Knetic/govaluate/sanitizedParameters.go create mode 100644 vendor/github.com/Knetic/govaluate/stagePlanner.go create mode 100755 vendor/github.com/Knetic/govaluate/test.sh create mode 100644 vendor/github.com/Knetic/govaluate/tokenStream.go create mode 100644 vendor/github.com/casbin/casbin/LICENSE create mode 100644 vendor/github.com/casbin/casbin/README.md create mode 100644 vendor/github.com/casbin/casbin/casbin-logo.png create mode 100644 vendor/github.com/casbin/casbin/effect/default_effector.go create mode 100644 vendor/github.com/casbin/casbin/effect/effector.go create mode 100644 vendor/github.com/casbin/casbin/enforcer.go create mode 100644 vendor/github.com/casbin/casbin/enforcer_cached.go create mode 100644 vendor/github.com/casbin/casbin/enforcer_safe.go create mode 100644 vendor/github.com/casbin/casbin/enforcer_synced.go create mode 100644 vendor/github.com/casbin/casbin/internal_api.go create mode 100644 vendor/github.com/casbin/casbin/log/default_logger.go create mode 100644 vendor/github.com/casbin/casbin/log/log_util.go create mode 100644 vendor/github.com/casbin/casbin/log/logger.go create mode 100644 vendor/github.com/casbin/casbin/management_api.go create mode 100644 vendor/github.com/casbin/casbin/model/assertion.go create mode 100644 vendor/github.com/casbin/casbin/model/function.go create mode 100644 vendor/github.com/casbin/casbin/model/model.go create mode 100644 vendor/github.com/casbin/casbin/model/policy.go create mode 100644 vendor/github.com/casbin/casbin/persist/adapter.go create mode 100644 vendor/github.com/casbin/casbin/persist/adapter_filtered.go create mode 100644 vendor/github.com/casbin/casbin/persist/file-adapter/adapter.go create mode 100644 vendor/github.com/casbin/casbin/persist/file-adapter/adapter_filtered.go create mode 100644 vendor/github.com/casbin/casbin/persist/file-adapter/adapter_mock.go create mode 100644 vendor/github.com/casbin/casbin/persist/watcher.go create mode 100644 vendor/github.com/casbin/casbin/rbac/default-role-manager/role_manager.go create mode 100644 vendor/github.com/casbin/casbin/rbac/role_manager.go create mode 100644 vendor/github.com/casbin/casbin/rbac_api.go create mode 100644 vendor/github.com/casbin/casbin/rbac_api_synced.go create mode 100644 vendor/github.com/casbin/casbin/rbac_api_with_domains.go create mode 100644 vendor/github.com/casbin/casbin/util/builtin_operators.go create mode 100644 vendor/github.com/casbin/casbin/util/util.go diff --git a/Gopkg.lock b/Gopkg.lock index c0081f6fc..aabe4fbc8 100644 --- a/Gopkg.lock +++ b/Gopkg.lock @@ -32,7 +32,7 @@ [[projects]] branch = "master" - digest = "1:9552f97d556ffa281b8d5c23280aad519e9792a02222cded90467882ff675063" + digest = "1:baa85b6c43f7fa2d05914718fda404f6ce9a63aed243809c4090326730f0ecd8" name = "github.com/CovenantSQL/sqlparser" packages = [ ".", @@ -42,7 +42,7 @@ "dependency/sqltypes", ] pruneopts = "UT" - revision = "21a792a5b3b3ecddc9a836fb3da0468f0bd0434f" + revision = "d9be292d3627ca3e62ec927e73b34a09b0b0b06d" [[projects]] branch = "master" @@ -52,6 +52,14 @@ pruneopts = "UT" revision = "b72b1571892216ba2a42fb8abeb726d082b4c4d1" +[[projects]] + digest = "1:5d3e23515e7916c152cc665eda0f7eaf6fdf8fdfe7c3dbac97049bcbd649b33f" + name = "github.com/Knetic/govaluate" + packages = ["."] + pruneopts = "UT" + revision = "d216395917cc49052c7c7094cf57f09657ca08a8" + version = "v3.0.0" + [[projects]] digest = "1:26777bf96d1c96d9be38c7c9795196b088c91c597ec5dae5bf6c02dc3c3520e9" name = "github.com/alecthomas/chroma" @@ -124,6 +132,25 @@ pruneopts = "UT" revision = "ab6388e0c60ae4834a1f57511e20c17b5f78be4b" +[[projects]] + digest = "1:76ca0dfcbf951d1868c7449453981dba9e1f79034706d1500a5a785000f5f222" + name = "github.com/casbin/casbin" + packages = [ + ".", + "config", + "effect", + "log", + "model", + "persist", + "persist/file-adapter", + "rbac", + "rbac/default-role-manager", + "util", + ] + pruneopts = "UT" + revision = "542e16cac74562eefac970a7d0d1467640d1f1cb" + version = "v1.7.0" + [[projects]] digest = "1:c28625428387b63dd7154eb857f51e700465cfbf7c06f619e71f2da33cefe47e" name = "github.com/coreos/bbolt" @@ -373,7 +400,7 @@ "version", ] pruneopts = "UT" - revision = "1f2c4f3cd6db5fd6f68f36af6b6d5d936fd93c4e" + revision = "b36ad289a3eaecdc52470a19591146a2c0ffb532" [[projects]] branch = "master" @@ -605,14 +632,14 @@ [[projects]] branch = "master" - digest = "1:a089387bbfcfa884e5aafeebb85929993195bb8959766b1d20102841d47e0b0e" + digest = "1:48b757e61d662021f49a6a0afc67dacae27ee0c3a8f858d927126bcdf49222d3" name = "golang.org/x/sys" packages = [ "unix", "windows", ] pruneopts = "UT" - revision = "93218def8b18e66adbdab3eca8ec334700329f1f" + revision = "ec83556a53fe16b65c452a104ea9d1e86a671852" [[projects]] digest = "1:342378ac4dcb378a5448dd723f0784ae519383532f5e70ade24132c4c8693202" @@ -636,6 +663,9 @@ "github.com/beevik/ntp", "github.com/btcsuite/btcd/btcec", "github.com/btcsuite/btcutil/base58", + "github.com/casbin/casbin", + "github.com/casbin/casbin/model", + "github.com/casbin/casbin/persist", "github.com/coreos/bbolt", "github.com/cyberdelia/go-metrics-graphite", "github.com/dyatlov/go-opengraph/opengraph", diff --git a/vendor/github.com/CovenantSQL/sqlparser/ast.go b/vendor/github.com/CovenantSQL/sqlparser/ast.go index c850c5006..eb3735cc7 100644 --- a/vendor/github.com/CovenantSQL/sqlparser/ast.go +++ b/vendor/github.com/CovenantSQL/sqlparser/ast.go @@ -207,16 +207,17 @@ type Statement interface { SQLNode } -func (*Union) iStatement() {} -func (*Select) iStatement() {} -func (*Stream) iStatement() {} -func (*Insert) iStatement() {} -func (*Update) iStatement() {} -func (*Delete) iStatement() {} -func (*Set) iStatement() {} -func (*DBDDL) iStatement() {} -func (*DDL) iStatement() {} -func (*Show) iStatement() {} +func (*Union) iStatement() {} +func (*Select) iStatement() {} +func (*Stream) iStatement() {} +func (*Insert) iStatement() {} +func (*Update) iStatement() {} +func (*Delete) iStatement() {} +func (*Set) iStatement() {} +func (*DBDDL) iStatement() {} +func (*DDL) iStatement() {} +func (*Show) iStatement() {} +func (*Explain) iStatement() {} // ParenSelect can actually not be a top level statement, // but we have to allow it because it's a requirement @@ -1273,6 +1274,19 @@ func (node *Show) walkSubtree(visit Visit) error { return nil } +// Explain represents a explain statement. +type Explain struct { +} + +// Format formats the node. +func (node *Explain) Format(buf *TrackedBuffer) { + buf.WriteString("explain") +} + +func (node *Explain) walkSubtree(visit Visit) error { + return nil +} + // Comments represents a list of comments. type Comments [][]byte diff --git a/vendor/github.com/CovenantSQL/sqlparser/sql.go b/vendor/github.com/CovenantSQL/sqlparser/sql.go index 54f4cdef6..2240f95b0 100644 --- a/vendor/github.com/CovenantSQL/sqlparser/sql.go +++ b/vendor/github.com/CovenantSQL/sqlparser/sql.go @@ -179,48 +179,49 @@ const SHOW = 57433 const DESCRIBE = 57434 const DATE = 57435 const ESCAPE = 57436 -const TINYINT = 57437 -const SMALLINT = 57438 -const MEDIUMINT = 57439 -const INT = 57440 -const INTEGER = 57441 -const BIGINT = 57442 -const INTNUM = 57443 -const REAL = 57444 -const DOUBLE = 57445 -const FLOAT_TYPE = 57446 -const DECIMAL = 57447 -const NUMERIC = 57448 -const TIME = 57449 -const TIMESTAMP = 57450 -const DATETIME = 57451 -const YEAR = 57452 -const CHAR = 57453 -const VARCHAR = 57454 -const BOOL = 57455 -const NCHAR = 57456 -const TEXT = 57457 -const TINYTEXT = 57458 -const MEDIUMTEXT = 57459 -const LONGTEXT = 57460 -const BLOB = 57461 -const TINYBLOB = 57462 -const MEDIUMBLOB = 57463 -const LONGBLOB = 57464 -const AUTO_INCREMENT = 57465 -const SIGNED = 57466 -const UNSIGNED = 57467 -const ZEROFILL = 57468 -const TABLES = 57469 -const CURRENT_TIMESTAMP = 57470 -const CURRENT_DATE = 57471 -const CURRENT_TIME = 57472 -const REPLACE = 57473 -const CAST = 57474 -const SUBSTR = 57475 -const GROUP_CONCAT = 57476 -const SEPARATOR = 57477 -const UNUSED = 57478 +const EXPLAIN = 57437 +const TINYINT = 57438 +const SMALLINT = 57439 +const MEDIUMINT = 57440 +const INT = 57441 +const INTEGER = 57442 +const BIGINT = 57443 +const INTNUM = 57444 +const REAL = 57445 +const DOUBLE = 57446 +const FLOAT_TYPE = 57447 +const DECIMAL = 57448 +const NUMERIC = 57449 +const TIME = 57450 +const TIMESTAMP = 57451 +const DATETIME = 57452 +const YEAR = 57453 +const CHAR = 57454 +const VARCHAR = 57455 +const BOOL = 57456 +const NCHAR = 57457 +const TEXT = 57458 +const TINYTEXT = 57459 +const MEDIUMTEXT = 57460 +const LONGTEXT = 57461 +const BLOB = 57462 +const TINYBLOB = 57463 +const MEDIUMBLOB = 57464 +const LONGBLOB = 57465 +const AUTO_INCREMENT = 57466 +const SIGNED = 57467 +const UNSIGNED = 57468 +const ZEROFILL = 57469 +const TABLES = 57470 +const CURRENT_TIMESTAMP = 57471 +const CURRENT_DATE = 57472 +const CURRENT_TIME = 57473 +const REPLACE = 57474 +const CAST = 57475 +const SUBSTR = 57476 +const GROUP_CONCAT = 57477 +const SEPARATOR = 57478 +const UNUSED = 57479 var yyToknames = [...]string{ "$end", @@ -334,6 +335,7 @@ var yyToknames = [...]string{ "DESCRIBE", "DATE", "ESCAPE", + "EXPLAIN", "TINYINT", "SMALLINT", "MEDIUMINT", @@ -392,559 +394,570 @@ var yyExca = [...]int{ -1, 3, 5, 17, -2, 4, - -1, 114, - 1, 156, - 5, 156, - 11, 156, - 12, 156, - 13, 156, - 14, 156, - 16, 156, - 27, 156, - 30, 156, - 31, 156, - 33, 156, - 35, 156, - 36, 156, - 37, 156, - 38, 156, - 40, 156, - 41, 156, - 154, 156, - -2, 169, - -1, 183, - 92, 356, - -2, 352, - -1, 184, + -1, 116, + 1, 157, + 5, 157, + 11, 157, + 12, 157, + 13, 157, + 14, 157, + 16, 157, + 27, 157, + 30, 157, + 31, 157, + 33, 157, + 35, 157, + 36, 157, + 37, 157, + 38, 157, + 40, 157, + 41, 157, + 155, 157, + -2, 170, + -1, 185, 92, 357, -2, 353, - -1, 402, + -1, 186, + 92, 358, + -2, 354, + -1, 405, 5, 17, - -2, 325, - -1, 519, - 92, 359, - -2, 355, - -1, 551, + -2, 326, + -1, 522, + 92, 360, + -2, 356, + -1, 554, 5, 18, - -2, 228, - -1, 609, + -2, 229, + -1, 612, 5, 18, - -2, 326, - -1, 672, + -2, 327, + -1, 675, 5, 17, - -2, 328, - -1, 732, - 5, 18, -2, 329, + -1, 735, + 5, 18, + -2, 330, } const yyPrivate = 57344 -const yyLast = 4079 +const yyLast = 4188 var yyAct = [...]int{ - 184, 381, 623, 136, 534, 429, 459, 514, 337, 535, - 142, 99, 31, 587, 428, 434, 207, 3, 406, 46, - 168, 526, 518, 94, 94, 489, 474, 147, 170, 440, - 405, 206, 119, 464, 546, 430, 335, 455, 94, 44, - 425, 210, 97, 186, 197, 134, 30, 111, 715, 138, - 132, 516, 566, 116, 110, 447, 627, 628, 629, 630, - 705, 122, 703, 466, 631, 684, 465, 123, 465, 109, - 126, 107, 638, 442, 84, 85, 91, 95, 121, 258, - 253, 252, 719, 128, 94, 83, 641, 353, 90, 94, - 560, 352, 351, 361, 362, 354, 355, 356, 357, 358, - 359, 360, 353, 612, 594, 125, 667, 94, 544, 139, - 187, 478, 380, 133, 341, 188, 94, 94, 94, 171, - 25, 189, 543, 94, 527, 588, 94, 86, 94, 94, - 340, 339, 88, 87, 94, 251, 339, 124, 527, 131, - 601, 444, 130, 685, 683, 25, 341, 441, 445, 262, - 344, 341, 448, 104, 496, 442, 260, 115, 632, 98, - 192, 374, 375, 376, 377, 378, 564, 494, 495, 493, - 193, 214, 120, 580, 581, 582, 250, 687, 686, 254, - 382, 256, 257, 249, 388, 569, 334, 24, 26, 15, - 16, 127, 94, 398, 568, 596, 94, 94, 94, 94, - 397, 340, 339, 28, 22, 94, 567, 402, 669, 94, - 421, 422, 94, 492, 731, 408, 46, 341, 720, 721, - 28, 625, 407, 137, 25, 423, 726, 395, 396, 441, - 120, 340, 339, 439, 438, 208, 433, 356, 357, 358, - 359, 360, 353, 561, 411, 404, 413, 341, 419, 435, - 410, 512, 412, 424, 462, 461, 680, 679, 725, 94, - 94, 340, 339, 371, 373, 354, 355, 356, 357, 358, - 359, 360, 353, 633, 18, 19, 20, 341, 471, 137, - 343, 636, 635, 457, 458, 611, 137, 467, 379, 21, - 23, 383, 384, 385, 386, 520, 389, 482, 484, 485, - 390, 391, 392, 342, 483, 520, 137, 195, 137, 607, - 403, 195, 468, 469, 32, 115, 115, 115, 115, 340, - 339, 216, 215, 472, 24, 426, 634, 542, 208, 27, - 597, 471, 554, 393, 194, 341, 199, 202, 46, 200, - 471, 201, 203, 547, 548, 400, 401, 195, 479, 480, - 24, 486, 487, 471, 542, 28, 488, 28, 476, 497, - 498, 499, 500, 501, 502, 503, 504, 505, 506, 507, - 508, 509, 510, 511, 449, 490, 450, 451, 460, 46, - 618, 24, 557, 28, 664, 521, 456, 452, 453, 454, - 547, 548, 382, 522, 40, 570, 519, 418, 202, 513, - 423, 433, 187, 671, 46, 12, 408, 416, 550, 415, - 408, 417, 414, 523, 28, 563, 517, 528, 724, 723, - 538, 408, 558, 463, 540, 255, 205, 551, 407, 46, - 536, 533, 524, 135, 555, 553, 105, 532, 552, 103, - 100, 694, 529, 530, 531, 101, 541, 549, 32, 433, - 651, 693, 660, 661, 662, 659, 426, 663, 640, 259, - 129, 117, 653, 34, 656, 658, 652, 94, 650, 559, - 36, 654, 29, 94, 199, 202, 46, 200, 1, 201, - 203, 655, 657, 89, 622, 437, 427, 118, 408, 39, - 149, 436, 682, 25, 519, 573, 476, 571, 626, 433, - 443, 579, 577, 383, 576, 584, 585, 586, 575, 565, - 446, 574, 562, 219, 517, 220, 590, 218, 591, 583, - 572, 537, 490, 25, 222, 221, 217, 649, 114, 595, - 688, 204, 603, 604, 605, 606, 602, 209, 473, 41, - 370, 608, 609, 610, 106, 677, 382, 600, 539, 394, - 185, 692, 615, 639, 613, 46, 617, 46, 46, 599, - 519, 614, 621, 408, 619, 433, 387, 592, 525, 620, - 407, 94, 148, 481, 158, 433, 598, 433, 624, 155, - 616, 351, 361, 362, 354, 355, 356, 357, 358, 359, - 360, 353, 115, 157, 156, 46, 646, 145, 642, 644, - 645, 647, 399, 345, 146, 140, 114, 113, 665, 190, - 668, 666, 46, 46, 198, 433, 196, 670, 545, 46, - 112, 46, 470, 714, 637, 676, 555, 672, 14, 33, - 648, 108, 674, 675, 11, 372, 536, 593, 46, 678, - 35, 433, 46, 10, 17, 9, 8, 169, 7, 6, - 5, 697, 4, 698, 696, 102, 37, 38, 433, 408, - 701, 13, 433, 2, 0, 96, 691, 713, 137, 712, - 92, 92, 0, 690, 0, 0, 717, 648, 716, 0, - 0, 46, 0, 722, 0, 92, 114, 114, 114, 114, - 718, 0, 0, 0, 0, 0, 728, 0, 0, 114, - 536, 624, 727, 382, 352, 351, 361, 362, 354, 355, - 356, 357, 358, 359, 360, 353, 0, 0, 730, 0, - 46, 46, 732, 0, 729, 0, 0, 537, 0, 0, - 673, 92, 0, 735, 0, 0, 92, 0, 0, 0, - 733, 734, 352, 351, 361, 362, 354, 355, 356, 357, - 358, 359, 360, 353, 92, 0, 0, 689, 0, 0, - 0, 0, 0, 92, 212, 92, 0, 0, 0, 0, - 92, 0, 0, 92, 0, 92, 92, 681, 0, 0, - 0, 261, 338, 137, 0, 0, 0, 0, 0, 0, - 0, 537, 0, 25, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 699, 0, 700, 0, 702, 0, 704, - 0, 706, 707, 708, 709, 710, 711, 0, 0, 352, - 351, 361, 362, 354, 355, 356, 357, 358, 359, 360, - 353, 0, 0, 0, 0, 0, 0, 491, 0, 92, - 643, 0, 409, 92, 92, 92, 92, 0, 0, 0, - 0, 0, 420, 0, 0, 0, 92, 0, 0, 212, - 352, 351, 361, 362, 354, 355, 356, 357, 358, 359, - 360, 353, 361, 362, 354, 355, 356, 357, 358, 359, - 360, 353, 0, 67, 0, 515, 0, 144, 0, 0, - 0, 0, 143, 0, 176, 59, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 28, 92, 92, 183, 160, - 159, 161, 162, 163, 164, 589, 0, 165, 166, 167, - 0, 0, 0, 0, 141, 153, 0, 175, 0, 0, - 0, 0, 0, 0, 0, 352, 351, 361, 362, 354, - 355, 356, 357, 358, 359, 360, 353, 150, 151, 336, - 0, 0, 0, 181, 0, 152, 0, 154, 0, 0, - 0, 0, 0, 114, 0, 0, 0, 0, 180, 0, - 68, 0, 0, 56, 0, 0, 51, 0, 76, 71, - 63, 57, 58, 47, 491, 69, 54, 55, 53, 66, - 73, 74, 52, 81, 50, 80, 49, 65, 72, 77, - 64, 61, 48, 75, 62, 60, 0, 70, 78, 82, - 0, 177, 178, 179, 182, 172, 173, 174, 0, 79, - 0, 0, 0, 0, 0, 0, 0, 338, 261, 0, - 0, 0, 0, 409, 67, 0, 0, 409, 338, 338, - 338, 0, 0, 0, 0, 0, 59, 0, 409, 0, - 0, 233, 0, 238, 239, 240, 241, 242, 243, 45, - 244, 245, 246, 247, 248, 234, 235, 236, 237, 223, - 224, 0, 0, 225, 226, 227, 228, 229, 230, 231, - 232, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 352, 351, 361, 362, 354, 355, - 356, 357, 358, 359, 360, 353, 0, 0, 0, 0, - 0, 0, 0, 0, 92, 409, 0, 0, 0, 338, - 92, 68, 0, 0, 56, 0, 261, 51, 0, 76, - 71, 63, 57, 58, 47, 0, 69, 54, 55, 53, - 66, 73, 74, 52, 81, 50, 80, 49, 65, 72, - 77, 64, 61, 48, 75, 62, 60, 0, 70, 78, - 82, 0, 0, 338, 0, 0, 0, 0, 0, 0, - 79, 0, 338, 0, 0, 0, 0, 0, 0, 0, + 186, 384, 626, 138, 537, 462, 32, 538, 172, 517, + 409, 432, 144, 101, 590, 431, 492, 437, 529, 47, + 208, 521, 170, 95, 95, 338, 443, 121, 477, 549, + 188, 467, 408, 433, 209, 3, 458, 212, 451, 95, + 31, 45, 199, 136, 428, 149, 718, 140, 113, 134, + 631, 632, 633, 569, 450, 112, 519, 634, 630, 111, + 124, 99, 355, 354, 364, 365, 357, 358, 359, 360, + 361, 362, 363, 356, 708, 706, 340, 469, 468, 109, + 445, 125, 468, 687, 128, 95, 641, 85, 86, 445, + 95, 123, 722, 91, 260, 130, 591, 122, 255, 254, + 25, 27, 15, 16, 644, 84, 563, 615, 597, 95, + 547, 190, 151, 481, 383, 135, 356, 22, 95, 95, + 95, 173, 26, 127, 344, 95, 546, 670, 95, 530, + 95, 95, 87, 29, 343, 342, 95, 89, 88, 133, + 342, 191, 359, 360, 361, 362, 363, 356, 26, 253, + 344, 116, 635, 264, 444, 344, 106, 262, 442, 441, + 117, 688, 686, 444, 100, 377, 378, 379, 380, 381, + 357, 358, 359, 360, 361, 362, 363, 356, 567, 141, + 189, 398, 399, 530, 251, 604, 346, 18, 19, 20, + 485, 487, 488, 337, 95, 401, 195, 486, 95, 95, + 95, 95, 21, 23, 400, 690, 24, 95, 499, 345, + 129, 95, 424, 425, 95, 343, 342, 447, 47, 411, + 347, 497, 498, 496, 448, 343, 342, 405, 26, 734, + 116, 344, 689, 572, 599, 583, 584, 585, 571, 210, + 436, 344, 410, 28, 570, 426, 438, 414, 422, 416, + 385, 427, 628, 413, 391, 415, 464, 723, 724, 375, + 122, 95, 95, 564, 453, 454, 515, 374, 376, 465, + 343, 342, 343, 342, 139, 455, 456, 457, 729, 672, + 29, 728, 683, 682, 460, 461, 344, 470, 344, 636, + 495, 523, 382, 474, 139, 386, 387, 388, 389, 429, + 392, 639, 638, 610, 393, 394, 395, 614, 139, 29, + 116, 116, 116, 116, 406, 523, 139, 197, 139, 117, + 117, 117, 117, 116, 218, 217, 197, 350, 545, 353, + 475, 637, 210, 33, 545, 366, 367, 368, 369, 370, + 371, 47, 351, 352, 349, 372, 355, 354, 364, 365, + 357, 358, 359, 360, 361, 362, 363, 356, 493, 474, + 474, 491, 600, 479, 500, 501, 502, 503, 504, 505, + 506, 507, 508, 509, 510, 511, 512, 513, 514, 474, + 557, 25, 47, 25, 396, 25, 452, 463, 524, 355, + 354, 364, 365, 357, 358, 359, 360, 361, 362, 363, + 356, 522, 403, 404, 516, 674, 436, 47, 621, 560, + 459, 411, 196, 539, 29, 411, 29, 520, 29, 482, + 483, 426, 489, 490, 573, 197, 411, 535, 41, 543, + 554, 12, 47, 527, 526, 555, 536, 553, 531, 550, + 551, 541, 419, 558, 139, 552, 420, 421, 204, 410, + 532, 533, 534, 544, 436, 201, 204, 418, 202, 417, + 203, 205, 494, 385, 525, 566, 105, 727, 726, 562, + 95, 561, 466, 189, 257, 207, 95, 137, 107, 47, + 355, 354, 364, 365, 357, 358, 359, 360, 361, 362, + 363, 356, 102, 411, 697, 574, 579, 103, 26, 522, + 578, 479, 33, 582, 436, 493, 556, 580, 386, 696, + 587, 588, 589, 586, 643, 520, 576, 36, 577, 593, + 429, 594, 261, 131, 201, 204, 540, 202, 26, 203, + 205, 550, 551, 38, 39, 606, 607, 608, 609, 119, + 35, 37, 97, 98, 611, 612, 613, 603, 30, 1, + 90, 616, 625, 440, 430, 120, 617, 40, 47, 439, + 47, 47, 685, 629, 446, 522, 568, 449, 411, 622, + 436, 624, 565, 623, 95, 595, 221, 222, 220, 224, + 436, 619, 436, 627, 601, 223, 219, 652, 116, 691, + 206, 410, 211, 476, 42, 373, 108, 117, 47, 649, + 598, 680, 645, 542, 647, 648, 650, 605, 669, 494, + 397, 187, 695, 668, 642, 47, 47, 385, 673, 539, + 436, 602, 47, 618, 47, 390, 528, 620, 150, 484, + 679, 651, 160, 157, 159, 558, 158, 677, 678, 147, + 402, 47, 596, 348, 681, 47, 436, 148, 675, 142, + 115, 192, 171, 200, 700, 198, 701, 548, 114, 699, + 473, 717, 704, 436, 411, 14, 684, 436, 34, 110, + 11, 10, 715, 17, 693, 93, 93, 9, 651, 720, + 8, 671, 7, 539, 47, 6, 725, 694, 5, 4, + 104, 93, 702, 13, 703, 2, 705, 0, 707, 731, + 709, 710, 711, 712, 713, 714, 627, 118, 0, 0, + 0, 721, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 733, 0, 47, 47, 735, 0, 0, 0, 732, + 92, 96, 540, 0, 0, 676, 738, 93, 0, 0, + 0, 667, 93, 0, 0, 736, 737, 0, 0, 719, + 354, 364, 365, 357, 358, 359, 360, 361, 362, 363, + 356, 93, 692, 0, 0, 0, 0, 0, 0, 0, + 93, 214, 93, 730, 385, 0, 0, 93, 0, 0, + 93, 0, 93, 93, 0, 0, 0, 0, 263, 341, + 0, 0, 126, 0, 0, 0, 540, 132, 26, 0, + 0, 0, 0, 0, 0, 0, 0, 654, 0, 0, + 663, 664, 665, 662, 0, 666, 194, 0, 0, 0, + 656, 0, 659, 661, 655, 0, 653, 216, 0, 657, + 716, 139, 252, 0, 0, 256, 0, 258, 259, 658, + 660, 0, 0, 0, 0, 0, 93, 0, 0, 412, + 93, 93, 93, 93, 0, 0, 0, 0, 0, 423, + 646, 0, 0, 93, 0, 0, 214, 355, 354, 364, + 365, 357, 358, 359, 360, 361, 362, 363, 356, 0, + 355, 354, 364, 365, 357, 358, 359, 360, 361, 362, + 363, 356, 0, 68, 0, 518, 0, 146, 0, 0, + 0, 407, 145, 0, 178, 60, 0, 0, 0, 0, + 0, 0, 0, 93, 93, 29, 0, 0, 185, 162, + 161, 163, 164, 165, 166, 592, 0, 167, 168, 169, + 0, 0, 0, 0, 143, 155, 0, 177, 0, 0, + 0, 0, 0, 0, 0, 355, 354, 364, 365, 357, + 358, 359, 360, 361, 362, 363, 356, 152, 153, 339, + 0, 0, 0, 183, 0, 154, 0, 156, 471, 472, + 0, 0, 0, 0, 0, 0, 0, 0, 182, 0, + 69, 0, 0, 57, 0, 0, 52, 0, 0, 77, + 72, 64, 58, 59, 48, 0, 70, 55, 56, 54, + 67, 74, 75, 53, 82, 51, 81, 50, 66, 73, + 78, 65, 62, 49, 76, 63, 61, 0, 71, 79, + 83, 0, 179, 180, 181, 184, 174, 175, 176, 0, + 80, 0, 0, 0, 0, 341, 263, 0, 0, 68, + 0, 412, 0, 0, 0, 412, 341, 341, 341, 0, + 0, 60, 0, 0, 0, 235, 412, 0, 240, 241, + 242, 243, 244, 245, 46, 246, 247, 248, 249, 250, + 236, 237, 238, 239, 225, 226, 0, 0, 227, 228, + 229, 230, 231, 232, 233, 234, 364, 365, 357, 358, + 359, 360, 361, 362, 363, 356, 0, 0, 0, 355, + 354, 364, 365, 357, 358, 359, 360, 361, 362, 363, + 356, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 93, 412, 0, 0, 69, 341, 93, 57, + 0, 0, 52, 0, 263, 77, 72, 64, 58, 59, + 48, 0, 70, 55, 56, 54, 67, 74, 75, 53, + 82, 51, 81, 50, 66, 73, 78, 65, 62, 49, + 76, 63, 61, 0, 71, 79, 83, 0, 0, 0, + 0, 341, 0, 0, 0, 0, 80, 575, 0, 0, + 341, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 412, 0, + 263, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 93, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 341, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 409, 0, 261, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 327, 318, 297, 329, 278, - 289, 333, 290, 291, 312, 269, 305, 67, 92, 281, - 265, 287, 266, 279, 299, 302, 277, 320, 331, 59, - 301, 303, 317, 296, 313, 272, 307, 310, 330, 0, - 0, 338, 45, 0, 431, 432, 0, 0, 0, 0, - 0, 309, 326, 288, 0, 0, 311, 264, 308, 0, - 268, 270, 332, 324, 284, 285, 556, 0, 0, 0, - 0, 0, 300, 304, 314, 294, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 282, 409, 306, 0, 0, - 0, 298, 0, 271, 0, 283, 315, 263, 322, 295, - 325, 293, 292, 328, 68, 0, 0, 56, 321, 280, - 51, 286, 76, 71, 63, 57, 58, 47, 0, 69, - 54, 55, 53, 66, 73, 74, 52, 81, 50, 80, - 49, 65, 72, 77, 64, 61, 48, 75, 62, 60, - 267, 70, 78, 82, 323, 275, 273, 274, 316, 0, - 276, 0, 319, 79, 327, 318, 297, 329, 278, 289, - 333, 290, 291, 312, 269, 305, 67, 0, 281, 265, - 287, 266, 279, 299, 302, 277, 320, 331, 59, 301, - 303, 317, 296, 313, 272, 307, 310, 330, 0, 0, - 0, 45, 0, 431, 432, 0, 0, 0, 0, 0, - 309, 326, 288, 0, 0, 311, 264, 308, 0, 268, - 270, 332, 324, 284, 285, 0, 0, 0, 0, 0, - 0, 300, 304, 314, 294, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 282, 0, 306, 0, 0, 0, - 298, 0, 271, 0, 283, 315, 263, 322, 295, 325, - 293, 292, 328, 68, 0, 0, 56, 321, 280, 51, - 286, 76, 71, 63, 57, 58, 47, 0, 69, 54, - 55, 53, 66, 73, 74, 52, 81, 50, 80, 49, - 65, 72, 77, 64, 61, 48, 75, 62, 60, 267, - 70, 78, 82, 323, 275, 273, 274, 316, 0, 276, - 0, 319, 79, 327, 318, 297, 329, 278, 289, 333, - 290, 291, 312, 269, 305, 67, 0, 281, 265, 287, - 266, 279, 299, 302, 277, 320, 331, 59, 301, 303, - 317, 296, 313, 272, 307, 310, 330, 0, 0, 0, - 45, 0, 0, 0, 0, 0, 0, 0, 0, 309, - 326, 288, 0, 0, 311, 264, 308, 0, 268, 270, - 332, 324, 284, 285, 0, 0, 0, 0, 0, 0, - 300, 304, 314, 294, 0, 0, 0, 0, 0, 0, - 0, 695, 0, 282, 0, 306, 0, 0, 0, 298, - 0, 271, 0, 283, 315, 263, 322, 295, 325, 293, - 292, 328, 68, 0, 0, 56, 321, 280, 51, 286, - 76, 71, 63, 57, 58, 47, 0, 69, 54, 55, - 53, 66, 73, 74, 52, 81, 50, 80, 49, 65, - 72, 77, 64, 61, 48, 75, 62, 60, 267, 70, - 78, 82, 323, 275, 273, 274, 316, 0, 276, 0, - 319, 79, 327, 318, 297, 329, 278, 289, 333, 290, - 291, 312, 269, 305, 67, 0, 281, 265, 287, 266, - 279, 299, 302, 277, 320, 331, 59, 301, 303, 317, - 296, 313, 272, 307, 310, 330, 28, 0, 0, 45, - 0, 0, 0, 0, 0, 0, 0, 0, 309, 326, - 288, 0, 0, 311, 264, 308, 0, 268, 270, 332, - 324, 284, 285, 0, 0, 0, 0, 0, 0, 300, - 304, 314, 294, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 282, 0, 306, 0, 0, 0, 298, 0, - 271, 0, 283, 315, 263, 322, 295, 325, 293, 292, - 328, 68, 0, 0, 56, 321, 280, 51, 286, 76, - 71, 63, 57, 58, 47, 0, 69, 54, 55, 53, - 66, 73, 74, 52, 81, 50, 80, 49, 65, 72, - 77, 64, 61, 48, 75, 62, 60, 267, 70, 78, - 82, 323, 275, 273, 274, 316, 0, 276, 0, 319, - 79, 327, 318, 297, 329, 278, 289, 333, 290, 291, - 312, 269, 305, 67, 0, 281, 265, 287, 266, 279, - 299, 302, 277, 320, 331, 59, 301, 303, 317, 296, - 313, 272, 307, 310, 330, 0, 0, 0, 183, 0, - 0, 0, 0, 0, 0, 0, 0, 309, 326, 288, - 0, 0, 311, 264, 308, 0, 268, 270, 332, 324, - 284, 285, 0, 0, 0, 0, 0, 0, 300, 304, - 314, 294, 0, 0, 0, 0, 0, 0, 0, 578, - 0, 282, 0, 306, 0, 0, 0, 298, 0, 271, - 0, 283, 315, 263, 322, 295, 325, 293, 292, 328, - 68, 0, 0, 56, 321, 280, 51, 286, 76, 71, - 63, 57, 58, 47, 0, 69, 54, 55, 53, 66, - 73, 74, 52, 81, 50, 80, 49, 65, 72, 77, - 64, 61, 48, 75, 62, 60, 267, 70, 78, 82, - 323, 275, 273, 274, 316, 0, 276, 0, 319, 79, - 327, 318, 297, 329, 278, 289, 333, 290, 291, 312, - 269, 305, 67, 0, 281, 265, 287, 266, 279, 299, - 302, 277, 320, 331, 59, 301, 303, 317, 296, 313, - 272, 307, 310, 330, 0, 0, 0, 45, 0, 0, - 0, 0, 0, 0, 0, 0, 309, 326, 288, 0, - 0, 311, 264, 308, 0, 268, 270, 332, 324, 284, - 285, 0, 0, 0, 0, 0, 0, 300, 304, 314, - 294, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 282, 0, 306, 0, 0, 0, 298, 0, 271, 0, - 283, 315, 263, 322, 295, 325, 293, 292, 328, 68, - 0, 0, 56, 321, 280, 51, 286, 76, 71, 63, - 57, 58, 47, 0, 69, 54, 55, 53, 66, 73, - 74, 52, 81, 50, 80, 49, 65, 72, 77, 64, - 61, 48, 75, 62, 60, 267, 70, 78, 82, 323, - 275, 273, 274, 316, 0, 276, 0, 319, 79, 327, - 318, 297, 329, 278, 289, 333, 290, 291, 312, 269, - 305, 67, 0, 281, 265, 287, 266, 279, 299, 302, - 277, 320, 331, 59, 301, 303, 317, 296, 313, 272, - 307, 310, 330, 0, 0, 0, 183, 0, 0, 0, - 0, 0, 0, 0, 0, 309, 326, 288, 0, 0, - 311, 264, 308, 0, 268, 270, 332, 324, 284, 285, - 0, 0, 0, 0, 0, 0, 300, 304, 314, 294, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 282, - 0, 306, 0, 0, 0, 298, 0, 271, 0, 283, - 315, 263, 322, 295, 325, 293, 292, 328, 68, 0, - 0, 56, 321, 280, 51, 286, 76, 71, 63, 57, - 58, 47, 0, 69, 54, 55, 53, 66, 73, 74, - 52, 81, 50, 80, 49, 65, 72, 77, 64, 61, - 48, 75, 62, 60, 267, 70, 78, 82, 323, 275, - 273, 274, 316, 0, 276, 0, 319, 79, 327, 318, - 297, 329, 278, 289, 333, 290, 291, 312, 269, 305, - 67, 0, 281, 265, 287, 266, 279, 299, 302, 277, - 320, 331, 59, 301, 303, 317, 296, 313, 272, 307, - 310, 330, 0, 0, 0, 93, 0, 0, 0, 0, - 0, 0, 0, 0, 309, 326, 288, 0, 0, 311, - 264, 308, 0, 268, 270, 332, 324, 284, 285, 0, - 0, 0, 0, 0, 0, 300, 304, 314, 294, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 282, 0, - 306, 0, 0, 0, 298, 0, 271, 0, 283, 315, - 263, 322, 295, 325, 293, 292, 328, 68, 0, 0, - 56, 321, 280, 51, 286, 76, 71, 63, 57, 58, - 47, 0, 69, 54, 55, 53, 66, 73, 74, 52, - 81, 50, 80, 49, 65, 72, 77, 64, 61, 48, - 75, 62, 60, 267, 70, 78, 82, 323, 275, 273, - 274, 316, 0, 276, 67, 319, 79, 0, 144, 0, - 0, 0, 0, 143, 0, 176, 59, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 28, 0, 0, 183, - 160, 159, 161, 162, 163, 164, 0, 0, 165, 166, - 167, 0, 0, 0, 0, 141, 153, 0, 175, 0, + 0, 640, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 412, 329, 320, 299, 331, 280, + 291, 335, 292, 293, 314, 271, 307, 68, 0, 283, + 267, 289, 268, 281, 301, 304, 279, 322, 333, 60, + 303, 305, 319, 298, 315, 274, 309, 312, 332, 0, + 0, 0, 46, 0, 434, 435, 0, 0, 0, 0, + 0, 311, 328, 290, 0, 0, 313, 266, 310, 0, + 270, 272, 334, 326, 286, 287, 559, 0, 0, 0, + 0, 0, 302, 306, 316, 296, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 284, 0, 308, 0, 0, + 0, 300, 0, 273, 0, 285, 317, 265, 324, 297, + 327, 295, 294, 330, 69, 0, 0, 57, 323, 282, + 52, 288, 336, 77, 72, 64, 58, 59, 48, 0, + 70, 55, 56, 54, 67, 74, 75, 53, 82, 51, + 81, 50, 66, 73, 78, 65, 62, 49, 76, 63, + 61, 269, 71, 79, 83, 325, 277, 275, 276, 318, + 0, 278, 0, 321, 80, 329, 320, 299, 331, 280, + 291, 335, 292, 293, 314, 271, 307, 68, 0, 283, + 267, 289, 268, 281, 301, 304, 279, 322, 333, 60, + 303, 305, 319, 298, 315, 274, 309, 312, 332, 0, + 0, 0, 46, 0, 434, 435, 0, 0, 0, 0, + 0, 311, 328, 290, 0, 0, 313, 266, 310, 0, + 270, 272, 334, 326, 286, 287, 0, 0, 0, 0, + 0, 0, 302, 306, 316, 296, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 284, 0, 308, 0, 0, + 0, 300, 0, 273, 0, 285, 317, 265, 324, 297, + 327, 295, 294, 330, 69, 0, 0, 57, 323, 282, + 52, 288, 336, 77, 72, 64, 58, 59, 48, 0, + 70, 55, 56, 54, 67, 74, 75, 53, 82, 51, + 81, 50, 66, 73, 78, 65, 62, 49, 76, 63, + 61, 269, 71, 79, 83, 325, 277, 275, 276, 318, + 0, 278, 0, 321, 80, 329, 320, 299, 331, 280, + 291, 335, 292, 293, 314, 271, 307, 68, 0, 283, + 267, 289, 268, 281, 301, 304, 279, 322, 333, 60, + 303, 305, 319, 298, 315, 274, 309, 312, 332, 0, + 0, 0, 46, 0, 0, 0, 0, 0, 0, 0, + 0, 311, 328, 290, 0, 0, 313, 266, 310, 0, + 270, 272, 334, 326, 286, 287, 0, 0, 0, 0, + 0, 0, 302, 306, 316, 296, 0, 0, 0, 0, + 0, 0, 0, 698, 0, 284, 0, 308, 0, 0, + 0, 300, 0, 273, 0, 285, 317, 265, 324, 297, + 327, 295, 294, 330, 69, 0, 0, 57, 323, 282, + 52, 288, 336, 77, 72, 64, 58, 59, 48, 0, + 70, 55, 56, 54, 67, 74, 75, 53, 82, 51, + 81, 50, 66, 73, 78, 65, 62, 49, 76, 63, + 61, 269, 71, 79, 83, 325, 277, 275, 276, 318, + 0, 278, 0, 321, 80, 329, 320, 299, 331, 280, + 291, 335, 292, 293, 314, 271, 307, 68, 0, 283, + 267, 289, 268, 281, 301, 304, 279, 322, 333, 60, + 303, 305, 319, 298, 315, 274, 309, 312, 332, 29, + 0, 0, 46, 0, 0, 0, 0, 0, 0, 0, + 0, 311, 328, 290, 0, 0, 313, 266, 310, 0, + 270, 272, 334, 326, 286, 287, 0, 0, 0, 0, + 0, 0, 302, 306, 316, 296, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 284, 0, 308, 0, 0, + 0, 300, 0, 273, 0, 285, 317, 265, 324, 297, + 327, 295, 294, 330, 69, 0, 0, 57, 323, 282, + 52, 288, 336, 77, 72, 64, 58, 59, 48, 0, + 70, 55, 56, 54, 67, 74, 75, 53, 82, 51, + 81, 50, 66, 73, 78, 65, 62, 49, 76, 63, + 61, 269, 71, 79, 83, 325, 277, 275, 276, 318, + 0, 278, 0, 321, 80, 329, 320, 299, 331, 280, + 291, 335, 292, 293, 314, 271, 307, 68, 0, 283, + 267, 289, 268, 281, 301, 304, 279, 322, 333, 60, + 303, 305, 319, 298, 315, 274, 309, 312, 332, 0, + 0, 0, 185, 0, 0, 0, 0, 0, 0, 0, + 0, 311, 328, 290, 0, 0, 313, 266, 310, 0, + 270, 272, 334, 326, 286, 287, 0, 0, 0, 0, + 0, 0, 302, 306, 316, 296, 0, 0, 0, 0, + 0, 0, 0, 581, 0, 284, 0, 308, 0, 0, + 0, 300, 0, 273, 0, 285, 317, 265, 324, 297, + 327, 295, 294, 330, 69, 0, 0, 57, 323, 282, + 52, 288, 336, 77, 72, 64, 58, 59, 48, 0, + 70, 55, 56, 54, 67, 74, 75, 53, 82, 51, + 81, 50, 66, 73, 78, 65, 62, 49, 76, 63, + 61, 269, 71, 79, 83, 325, 277, 275, 276, 318, + 0, 278, 0, 321, 80, 329, 320, 299, 331, 280, + 291, 335, 292, 293, 314, 271, 307, 68, 0, 283, + 267, 289, 268, 281, 301, 304, 279, 322, 333, 60, + 303, 305, 319, 298, 315, 274, 309, 312, 332, 0, + 0, 0, 46, 0, 0, 0, 0, 0, 0, 0, + 0, 311, 328, 290, 0, 0, 313, 266, 310, 0, + 270, 272, 334, 326, 286, 287, 0, 0, 0, 0, + 0, 0, 302, 306, 316, 296, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 284, 0, 308, 0, 0, + 0, 300, 0, 273, 0, 285, 317, 265, 324, 297, + 327, 295, 294, 330, 69, 0, 0, 57, 323, 282, + 52, 288, 336, 77, 72, 64, 58, 59, 48, 0, + 70, 55, 56, 54, 67, 74, 75, 53, 82, 51, + 81, 50, 66, 73, 78, 65, 62, 49, 76, 63, + 61, 269, 71, 79, 83, 325, 277, 275, 276, 318, + 0, 278, 0, 321, 80, 329, 320, 299, 331, 280, + 291, 335, 292, 293, 314, 271, 307, 68, 0, 283, + 267, 289, 268, 281, 301, 304, 279, 322, 333, 60, + 303, 305, 319, 298, 315, 274, 309, 312, 332, 0, + 0, 0, 185, 0, 0, 0, 0, 0, 0, 0, + 0, 311, 328, 290, 0, 0, 313, 266, 310, 0, + 270, 272, 334, 326, 286, 287, 0, 0, 0, 0, + 0, 0, 302, 306, 316, 296, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 284, 0, 308, 0, 0, + 0, 300, 0, 273, 0, 285, 317, 265, 324, 297, + 327, 295, 294, 330, 69, 0, 0, 57, 323, 282, + 52, 288, 336, 77, 72, 64, 58, 59, 48, 0, + 70, 55, 56, 54, 67, 74, 75, 53, 82, 51, + 81, 50, 66, 73, 78, 65, 62, 49, 76, 63, + 61, 269, 71, 79, 83, 325, 277, 275, 276, 318, + 0, 278, 0, 321, 80, 329, 320, 299, 331, 280, + 291, 335, 292, 293, 314, 271, 307, 68, 0, 283, + 267, 289, 268, 281, 301, 304, 279, 322, 333, 60, + 303, 305, 319, 298, 315, 274, 309, 312, 332, 0, + 0, 0, 94, 0, 0, 0, 0, 0, 0, 0, + 0, 311, 328, 290, 0, 0, 313, 266, 310, 0, + 270, 272, 334, 326, 286, 287, 0, 0, 0, 0, + 0, 0, 302, 306, 316, 296, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 284, 0, 308, 0, 0, + 0, 300, 0, 273, 0, 285, 317, 265, 324, 297, + 327, 295, 294, 330, 69, 0, 0, 57, 323, 282, + 52, 288, 336, 77, 72, 64, 58, 59, 48, 0, + 70, 55, 56, 54, 67, 74, 75, 53, 82, 51, + 81, 50, 66, 73, 78, 65, 62, 49, 76, 63, + 61, 269, 71, 79, 83, 325, 277, 275, 276, 318, + 0, 278, 68, 321, 80, 0, 146, 0, 0, 0, + 0, 145, 0, 178, 60, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 29, 0, 0, 185, 162, 161, + 163, 164, 165, 166, 0, 0, 167, 168, 169, 0, + 0, 0, 0, 143, 155, 0, 177, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 150, 151, - 336, 0, 0, 0, 181, 0, 152, 0, 154, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 180, - 0, 68, 0, 0, 56, 0, 0, 51, 0, 76, - 71, 63, 57, 58, 47, 0, 69, 54, 55, 53, - 66, 73, 74, 52, 81, 50, 80, 49, 65, 72, - 77, 64, 61, 48, 75, 62, 60, 0, 70, 78, - 82, 0, 177, 178, 179, 182, 172, 173, 174, 67, - 79, 0, 0, 144, 0, 0, 0, 0, 143, 0, - 176, 59, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 28, 0, 137, 183, 160, 159, 161, 162, 163, - 164, 0, 0, 165, 166, 167, 0, 0, 0, 0, - 141, 153, 0, 175, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 152, 153, 339, 0, + 0, 0, 183, 0, 154, 0, 156, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 182, 0, 69, + 0, 0, 57, 0, 0, 52, 0, 0, 77, 72, + 64, 58, 59, 48, 0, 70, 55, 56, 54, 67, + 74, 75, 53, 82, 51, 81, 50, 66, 73, 78, + 65, 62, 49, 76, 63, 61, 0, 71, 79, 83, + 0, 179, 180, 181, 184, 174, 175, 176, 68, 80, + 0, 0, 146, 0, 0, 0, 0, 145, 0, 178, + 60, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 29, 0, 139, 185, 162, 161, 163, 164, 165, 166, + 0, 0, 167, 168, 169, 0, 0, 0, 0, 143, + 155, 0, 177, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 150, 151, 0, 0, 0, 0, 181, - 0, 152, 0, 154, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 180, 0, 68, 0, 0, 56, - 0, 0, 51, 0, 76, 71, 63, 57, 58, 47, - 0, 69, 54, 55, 53, 66, 73, 74, 52, 81, - 50, 80, 49, 65, 72, 77, 64, 61, 48, 75, - 62, 60, 0, 70, 78, 82, 24, 177, 178, 179, - 182, 172, 173, 174, 0, 79, 0, 67, 0, 0, - 0, 144, 0, 0, 0, 0, 143, 0, 176, 59, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 28, - 0, 0, 183, 160, 159, 161, 162, 163, 164, 0, - 0, 165, 166, 167, 0, 0, 0, 0, 141, 153, - 0, 175, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 152, 153, 0, 0, 0, 0, 183, 0, + 154, 0, 156, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 182, 0, 69, 0, 0, 57, 0, + 0, 52, 0, 0, 77, 72, 64, 58, 59, 48, + 0, 70, 55, 56, 54, 67, 74, 75, 53, 82, + 51, 81, 50, 66, 73, 78, 65, 62, 49, 76, + 63, 61, 0, 71, 79, 83, 25, 179, 180, 181, + 184, 174, 175, 176, 0, 80, 0, 68, 0, 0, + 0, 146, 0, 0, 0, 0, 145, 0, 178, 60, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 29, + 0, 0, 185, 162, 161, 163, 164, 165, 166, 0, + 0, 167, 168, 169, 0, 0, 0, 0, 143, 155, + 0, 177, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 150, 151, 0, 0, 0, 0, 181, 0, 152, - 0, 154, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 180, 0, 68, 0, 0, 56, 0, 0, - 51, 0, 76, 71, 63, 57, 58, 47, 0, 69, - 54, 55, 53, 66, 73, 74, 52, 81, 50, 80, - 49, 65, 72, 77, 64, 61, 48, 75, 62, 60, - 0, 70, 78, 82, 0, 177, 178, 179, 182, 172, - 173, 174, 67, 79, 0, 0, 144, 0, 0, 0, - 0, 143, 0, 176, 59, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 28, 0, 0, 183, 160, 159, - 161, 162, 163, 164, 0, 0, 165, 166, 167, 0, - 0, 0, 0, 141, 153, 0, 175, 0, 0, 0, + 0, 152, 153, 0, 0, 0, 0, 183, 0, 154, + 0, 156, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 182, 0, 69, 0, 0, 57, 0, 0, + 52, 0, 0, 77, 72, 64, 58, 59, 48, 0, + 70, 55, 56, 54, 67, 74, 75, 53, 82, 51, + 81, 50, 66, 73, 78, 65, 62, 49, 76, 63, + 61, 0, 71, 79, 83, 0, 179, 180, 181, 184, + 174, 175, 176, 68, 80, 0, 0, 146, 0, 0, + 0, 0, 145, 0, 178, 60, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 29, 0, 0, 185, 162, + 161, 163, 164, 165, 166, 0, 0, 167, 168, 169, + 0, 0, 0, 0, 143, 155, 0, 177, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 150, 151, 0, 0, - 0, 0, 181, 0, 152, 0, 154, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 180, 0, 68, - 0, 0, 56, 0, 0, 51, 0, 76, 71, 63, - 57, 58, 47, 0, 69, 54, 55, 53, 66, 73, - 74, 52, 81, 50, 80, 49, 65, 72, 77, 64, - 61, 48, 75, 62, 60, 0, 70, 78, 82, 67, - 177, 178, 179, 182, 172, 173, 174, 0, 79, 0, - 176, 59, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 28, 0, 0, 183, 160, 159, 161, 162, 163, - 164, 0, 0, 165, 166, 167, 0, 0, 0, 0, - 0, 153, 0, 175, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 152, 153, 0, + 0, 0, 0, 183, 0, 154, 0, 156, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 182, 0, + 69, 0, 0, 57, 0, 0, 52, 0, 0, 77, + 72, 64, 58, 59, 48, 0, 70, 55, 56, 54, + 67, 74, 75, 53, 82, 51, 81, 50, 66, 73, + 78, 65, 62, 49, 76, 63, 61, 0, 71, 79, + 83, 68, 179, 180, 181, 184, 174, 175, 176, 0, + 80, 0, 178, 60, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 29, 0, 0, 185, 162, 161, 163, + 164, 165, 166, 0, 0, 167, 168, 169, 0, 0, + 0, 0, 0, 155, 0, 177, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 150, 151, 0, 0, 0, 0, 181, - 0, 152, 0, 154, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 180, 0, 68, 0, 0, 56, - 0, 0, 51, 0, 76, 71, 63, 57, 58, 47, - 0, 69, 54, 55, 53, 66, 73, 74, 52, 81, - 50, 80, 49, 65, 72, 77, 64, 61, 48, 75, - 62, 60, 0, 70, 78, 82, 0, 177, 178, 179, - 182, 172, 173, 174, 67, 79, 0, 475, 0, 0, - 0, 0, 0, 0, 0, 0, 59, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 45, - 0, 477, 0, 0, 0, 24, 0, 0, 0, 0, - 0, 0, 0, 340, 339, 0, 67, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 59, 341, - 0, 0, 0, 0, 0, 0, 0, 0, 28, 0, - 0, 45, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 152, 153, 0, 0, 0, + 0, 183, 0, 154, 0, 156, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 182, 0, 69, 0, + 0, 57, 0, 0, 52, 0, 0, 77, 72, 64, + 58, 59, 48, 0, 70, 55, 56, 54, 67, 74, + 75, 53, 82, 51, 81, 50, 66, 73, 78, 65, + 62, 49, 76, 63, 61, 0, 71, 79, 83, 0, + 179, 180, 181, 184, 174, 175, 176, 68, 80, 0, + 478, 0, 0, 0, 0, 0, 0, 0, 0, 60, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 68, 0, 0, 56, 0, 0, 51, 0, 76, - 71, 63, 57, 58, 47, 0, 69, 54, 55, 53, - 66, 73, 74, 52, 81, 50, 80, 49, 65, 72, - 77, 64, 61, 48, 75, 62, 60, 0, 70, 78, - 82, 0, 0, 68, 0, 0, 56, 0, 0, 51, - 79, 76, 71, 63, 57, 58, 47, 0, 69, 54, - 55, 53, 66, 73, 74, 52, 81, 50, 80, 49, - 65, 72, 77, 64, 61, 48, 75, 62, 60, 24, - 70, 78, 82, 0, 0, 0, 0, 0, 0, 0, - 67, 0, 79, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 59, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 28, 0, 0, 93, 0, 0, 0, 0, + 0, 0, 46, 0, 480, 0, 0, 0, 25, 0, + 0, 0, 0, 0, 0, 0, 343, 342, 0, 68, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 67, 0, 0, 211, 0, 0, 0, 0, - 0, 0, 0, 0, 59, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 93, 0, 213, + 0, 60, 344, 0, 0, 0, 0, 0, 0, 0, + 0, 29, 0, 0, 46, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 68, 0, 0, - 56, 0, 0, 51, 0, 76, 71, 63, 57, 58, - 47, 0, 69, 54, 55, 53, 66, 73, 74, 52, - 81, 50, 80, 49, 65, 72, 77, 64, 61, 48, - 75, 62, 60, 0, 70, 78, 82, 0, 0, 68, - 0, 0, 56, 0, 0, 51, 79, 76, 71, 63, - 57, 58, 47, 0, 69, 54, 55, 53, 66, 73, - 74, 52, 81, 50, 80, 49, 65, 72, 77, 64, - 61, 48, 75, 62, 60, 67, 70, 78, 82, 0, - 0, 0, 0, 0, 0, 0, 0, 59, 79, 0, + 0, 0, 0, 0, 69, 0, 0, 57, 0, 0, + 52, 0, 0, 77, 72, 64, 58, 59, 48, 0, + 70, 55, 56, 54, 67, 74, 75, 53, 82, 51, + 81, 50, 66, 73, 78, 65, 62, 49, 76, 63, + 61, 0, 71, 79, 83, 0, 69, 0, 0, 57, + 0, 0, 52, 0, 80, 77, 72, 64, 58, 59, + 48, 0, 70, 55, 56, 54, 67, 74, 75, 53, + 82, 51, 81, 50, 66, 73, 78, 65, 62, 49, + 76, 63, 61, 25, 71, 79, 83, 0, 0, 0, + 0, 0, 0, 0, 68, 0, 80, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 60, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 29, 0, 0, 94, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 45, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 67, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 59, + 0, 0, 0, 0, 0, 0, 68, 0, 0, 213, + 0, 0, 0, 0, 0, 0, 0, 0, 60, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 45, 0, 477, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 42, 0, 0, 0, - 0, 43, 68, 0, 0, 56, 0, 0, 51, 0, - 76, 71, 63, 57, 58, 47, 0, 69, 54, 55, - 53, 66, 73, 74, 52, 81, 50, 80, 49, 65, - 72, 77, 64, 61, 48, 75, 62, 60, 0, 70, - 78, 82, 0, 0, 68, 0, 0, 56, 0, 0, - 51, 79, 76, 71, 63, 57, 58, 47, 0, 69, - 54, 55, 53, 66, 73, 74, 52, 81, 50, 80, - 49, 65, 72, 77, 64, 61, 48, 75, 62, 60, - 67, 70, 78, 82, 0, 0, 0, 0, 0, 0, - 0, 0, 59, 79, 0, 0, 0, 0, 0, 0, - 0, 0, 28, 0, 0, 93, 0, 0, 0, 0, + 0, 94, 0, 215, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 67, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 59, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 93, 0, 213, + 0, 69, 0, 0, 57, 0, 0, 52, 0, 0, + 77, 72, 64, 58, 59, 48, 0, 70, 55, 56, + 54, 67, 74, 75, 53, 82, 51, 81, 50, 66, + 73, 78, 65, 62, 49, 76, 63, 61, 0, 71, + 79, 83, 0, 69, 0, 0, 57, 0, 0, 52, + 0, 80, 77, 72, 64, 58, 59, 48, 0, 70, + 55, 56, 54, 67, 74, 75, 53, 82, 51, 81, + 50, 66, 73, 78, 65, 62, 49, 76, 63, 61, + 68, 71, 79, 83, 0, 0, 0, 0, 0, 0, + 0, 0, 60, 80, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 46, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 68, 0, 0, - 56, 0, 0, 51, 0, 76, 71, 63, 57, 58, - 47, 0, 69, 54, 55, 53, 66, 73, 74, 52, - 81, 50, 80, 49, 65, 72, 77, 64, 61, 48, - 75, 62, 60, 0, 70, 78, 82, 0, 0, 68, - 0, 0, 56, 0, 0, 51, 79, 76, 71, 63, - 57, 58, 47, 0, 69, 54, 55, 53, 66, 73, - 74, 52, 81, 50, 80, 49, 65, 72, 77, 64, - 61, 48, 75, 62, 60, 67, 70, 78, 82, 0, - 0, 0, 191, 0, 0, 0, 0, 59, 79, 0, + 0, 0, 68, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 60, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 46, 0, 480, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 93, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 67, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 347, 59, - 350, 0, 0, 0, 0, 0, 363, 364, 365, 366, - 367, 368, 45, 348, 349, 346, 369, 352, 351, 361, - 362, 354, 355, 356, 357, 358, 359, 360, 353, 0, - 0, 0, 68, 0, 0, 56, 0, 0, 51, 0, - 76, 71, 63, 57, 58, 47, 0, 69, 54, 55, - 53, 66, 73, 74, 52, 81, 50, 80, 49, 65, - 72, 77, 64, 61, 48, 75, 62, 60, 0, 70, - 78, 82, 0, 0, 68, 0, 0, 56, 0, 0, - 51, 79, 76, 71, 63, 57, 58, 47, 0, 69, - 54, 55, 53, 66, 73, 74, 52, 81, 50, 80, - 49, 65, 72, 77, 64, 61, 48, 75, 62, 60, - 67, 70, 78, 82, 0, 0, 0, 0, 0, 0, - 0, 0, 59, 79, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 183, 0, 0, 0, 0, + 0, 43, 0, 0, 0, 0, 44, 69, 0, 0, + 57, 0, 0, 52, 0, 0, 77, 72, 64, 58, + 59, 48, 0, 70, 55, 56, 54, 67, 74, 75, + 53, 82, 51, 81, 50, 66, 73, 78, 65, 62, + 49, 76, 63, 61, 0, 71, 79, 83, 0, 69, + 0, 0, 57, 0, 0, 52, 0, 80, 77, 72, + 64, 58, 59, 48, 0, 70, 55, 56, 54, 67, + 74, 75, 53, 82, 51, 81, 50, 66, 73, 78, + 65, 62, 49, 76, 63, 61, 68, 71, 79, 83, + 0, 0, 0, 0, 0, 0, 0, 0, 60, 80, + 0, 0, 0, 0, 0, 0, 0, 0, 29, 0, + 0, 94, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 68, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 67, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 59, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 93, 0, 0, + 60, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 94, 0, 215, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 68, 0, 0, - 56, 0, 0, 51, 0, 76, 71, 63, 57, 58, - 47, 0, 69, 54, 55, 53, 66, 73, 74, 52, - 81, 50, 80, 49, 65, 72, 77, 64, 61, 48, - 75, 62, 60, 0, 70, 78, 82, 0, 0, 68, - 0, 0, 56, 0, 0, 51, 79, 76, 71, 63, - 57, 58, 47, 0, 69, 54, 55, 53, 66, 73, - 74, 52, 81, 50, 80, 49, 65, 72, 77, 64, - 61, 48, 75, 62, 60, 0, 70, 78, 82, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 79, + 0, 0, 0, 69, 0, 0, 57, 0, 0, 52, + 0, 0, 77, 72, 64, 58, 59, 48, 0, 70, + 55, 56, 54, 67, 74, 75, 53, 82, 51, 81, + 50, 66, 73, 78, 65, 62, 49, 76, 63, 61, + 0, 71, 79, 83, 0, 69, 0, 0, 57, 0, + 0, 52, 0, 80, 77, 72, 64, 58, 59, 48, + 0, 70, 55, 56, 54, 67, 74, 75, 53, 82, + 51, 81, 50, 66, 73, 78, 65, 62, 49, 76, + 63, 61, 68, 71, 79, 83, 0, 0, 0, 193, + 0, 0, 0, 0, 60, 80, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 94, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 68, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 60, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 46, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 69, + 0, 0, 57, 0, 0, 52, 0, 0, 77, 72, + 64, 58, 59, 48, 0, 70, 55, 56, 54, 67, + 74, 75, 53, 82, 51, 81, 50, 66, 73, 78, + 65, 62, 49, 76, 63, 61, 0, 71, 79, 83, + 0, 69, 0, 0, 57, 0, 0, 52, 0, 80, + 77, 72, 64, 58, 59, 48, 0, 70, 55, 56, + 54, 67, 74, 75, 53, 82, 51, 81, 50, 66, + 73, 78, 65, 62, 49, 76, 63, 61, 68, 71, + 79, 83, 0, 0, 0, 0, 0, 0, 0, 0, + 60, 80, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 185, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 68, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 60, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 94, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 69, 0, 0, 57, 0, + 0, 52, 0, 0, 77, 72, 64, 58, 59, 48, + 0, 70, 55, 56, 54, 67, 74, 75, 53, 82, + 51, 81, 50, 66, 73, 78, 65, 62, 49, 76, + 63, 61, 0, 71, 79, 83, 0, 69, 0, 0, + 57, 0, 0, 52, 0, 80, 77, 72, 64, 58, + 59, 48, 0, 70, 55, 56, 54, 67, 74, 75, + 53, 82, 51, 81, 50, 66, 73, 78, 65, 62, + 49, 76, 63, 61, 0, 71, 79, 83, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 80, } var yyPact = [...]int{ - 181, -1000, -108, -1000, -1000, -1000, -1000, -1000, -1000, -1000, - -1000, -1000, 434, 458, -1000, -1000, -1000, 355, 3388, -13, - -24, 34, 3925, 3925, -1000, 344, 103, -1000, -1000, -1000, - -1000, 424, 430, 344, 418, -30, -1000, 3553, 451, -1000, - 188, -21, -35, -1000, -1000, -1000, -1000, -1000, -1000, -1000, + 94, -1000, -115, -1000, -1000, -1000, -1000, -1000, -1000, -1000, + -1000, -1000, 488, 535, -1000, -1000, -1000, 389, 3493, 7, + -11, 39, 4033, 4033, -1000, -1000, 379, 108, -1000, -1000, + -1000, -1000, 476, 482, 379, 460, -22, -1000, 3659, 529, + -1000, 218, -8, -21, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, - -1000, -1000, -1000, 3925, -32, -32, -15, 450, 3925, -5, - -1000, -1000, 21, -1000, -1000, -1000, 414, 182, -99, -1000, - 2785, 2785, 434, -1000, 344, -1000, 3718, -1000, 120, 307, - 444, -1000, -1000, -1000, 406, 3223, 3265, 3925, 281, -1000, - 941, -35, 3925, 77, -16, 3925, 404, 3925, 3925, -19, - -1000, -1000, 449, 2243, 2377, -1000, -1000, -1000, -1000, 263, - -1000, 2785, 3730, 316, 316, -1000, -1000, -1000, -1000, -1000, - 2912, 2912, 2912, 2912, 2912, -1000, -1000, -1000, -1000, -1000, - -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, 316, 20, - -1000, 2650, 316, 316, 316, 2785, 316, -1000, -1000, -1000, - 316, 316, 316, -1000, -1000, 293, -1000, 205, 424, 182, - 318, 3925, -1000, -1000, 3883, 3553, 3553, 3553, 3553, -1000, - 382, 379, 377, 367, 3925, -1000, 267, 182, 3223, -1000, - -1000, 3595, -1000, -1000, 445, 1349, 130, 90, -87, -1000, - -1000, 335, -1000, 335, 335, -1000, -1000, -1000, -1000, -1000, - -1000, -1000, -1000, -1000, 335, 335, 335, -1000, -1000, -1000, - -1000, -1000, -1000, -1000, 347, 347, 347, 339, 339, 212, - -1000, 402, -39, -37, -1000, -1000, -1000, -1000, 3925, 3925, + -1000, -1000, -1000, -1000, 4033, -18, -18, -3, 513, 4033, + -6, -1000, -1000, 23, -1000, -1000, -1000, -1000, 458, 233, + -102, -1000, 2886, 2886, 488, -1000, 379, -1000, 3825, -1000, + 146, 385, 425, -1000, -1000, -1000, 455, 3327, 3369, 4033, + 284, -1000, 945, -21, 4033, 91, 2, 4033, 453, 4033, + 4033, -4, -1000, -1000, 512, 2340, 2475, -1000, -1000, -1000, + -1000, 169, -1000, 2886, 269, 270, 270, -1000, -1000, -1000, + -1000, -1000, 3014, 3014, 3014, 3014, 3014, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, + 270, 22, -1000, 2750, 270, 270, 270, 2886, 270, -1000, + -1000, -1000, 270, 270, 270, -1000, -1000, 344, -1000, 159, + 476, 233, 375, 4033, -1000, -1000, 3991, 3659, 3659, 3659, + 3659, -1000, 429, 427, 412, 417, 4033, -1000, 277, 233, + 3327, -1000, -1000, 3701, -1000, -1000, 509, 1440, 55, 166, + -89, -1000, -1000, 347, -1000, 347, 347, -1000, -1000, -1000, + -1000, -1000, -1000, -1000, -1000, -1000, 347, 347, 347, -1000, + -1000, -1000, -1000, -1000, -1000, -1000, 371, 371, 371, 348, + 348, 227, -1000, 451, -27, -23, -1000, -1000, -1000, -1000, + 4033, 4033, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, - -1000, -1000, -1000, -1000, 313, -1000, -1000, 3047, 19, 2785, - 2785, 246, 2785, 2785, 42, 2912, 164, 94, 2912, 2912, - 2912, 2912, 2912, 2912, 2912, 2912, 2912, 2912, 2912, 2912, - 2912, 2912, 2912, -1000, -1000, -1000, -1000, -1000, -1000, -1000, - -1000, 209, -1000, 344, -1000, -1000, -1000, -1000, 1017, 866, - 2094, 265, 74, 2650, 2785, 3883, 414, 62, 74, 3883, - 2377, 2377, 2377, 2785, -1000, -1000, -1000, -1000, -1000, -1000, - 3883, 316, -1000, 3089, -1000, 314, -1000, 56, -1000, 16, - 444, 353, 306, -1000, -1000, -1000, -1000, 378, -1000, -1000, - -1000, -1000, -1000, 182, -1000, 434, 2785, 292, 1200, -1000, - -1000, -1000, -1000, -1000, -1000, -1000, -1000, 343, 397, 48, - 201, -1000, -1000, 389, -1000, 115, -91, -1000, -1000, 161, - -1000, -1000, -1000, -1000, -1000, -1000, 149, -1000, -1000, -1000, - 140, -1000, 358, -1000, 188, -1000, 3925, 3883, -1000, -1000, - 445, 2377, 3553, -1000, -1000, 3430, -1000, -1000, 1796, 42, - 79, -1000, -1000, 122, -1000, -1000, 74, 74, 665, -1000, - -1000, -1000, -1000, 164, 2912, 2912, 2912, 14, 665, 858, - 793, 503, -1000, 154, 154, -1, -1, -1, -1, -1, - 184, 184, 182, -1000, 182, 2377, 291, 316, 12, -1000, - 2785, -1000, 175, 290, 2377, 76, -1000, 2785, 182, 238, - 238, 238, -1000, 287, 269, -1000, -1000, 2512, 182, 245, - 11, 434, 3883, 2785, 2094, -1000, -1000, 2785, 341, -1000, - -1000, -1000, 424, 74, 1349, -1000, 1349, 3760, -1000, 179, - -1000, -1000, -84, 13, -1000, -1000, -1000, 232, 286, 241, - 3925, -1000, -1000, -28, 446, -1000, 271, -1000, -1000, -6, - -1000, -1000, -1000, -1000, 14, 665, 783, -1000, 2912, 2912, - -1000, -1000, 238, 2377, 1945, 74, 340, 2912, 300, 41, - -1000, 2785, 145, -1000, -1000, -1000, -1000, 316, -1000, -1000, - 375, 3760, 3760, 424, -1000, 74, -1000, 74, 3760, -1000, - 1200, -1000, 216, -1000, 335, -1000, 40, -1000, -1000, -1000, - -1000, -1000, -1000, -1000, 133, -1000, 132, 1647, 3883, 438, - 426, 1498, -1000, 2912, 665, 665, -1000, 182, -1000, 182, - 335, -1000, 335, 339, 335, -54, 335, -56, 335, 335, - 335, 335, 335, 335, -1000, 627, -104, -1000, 74, 2785, - -1000, 316, -1000, 344, -10, -1000, -1000, 178, -1000, -1000, - 3760, -1000, -1000, 394, -1000, 393, 217, 185, -1000, -1000, - -1000, -1000, -1000, 2785, 2785, -1000, 665, -1000, -1000, -1000, + -1000, -1000, -1000, -1000, -1000, -1000, -1000, 320, -1000, -1000, + 3150, 21, 2886, 2886, 139, 2886, 2886, 52, 3014, 241, + 148, 3014, 3014, 3014, 3014, 3014, 3014, 3014, 3014, 3014, + 3014, 3014, 3014, 3014, 3014, 3014, -1000, -1000, -1000, -1000, + -1000, -1000, -1000, -1000, 224, -1000, 379, -1000, -1000, -1000, + -1000, 1022, 876, 2190, 275, 78, 2750, 2886, 3991, 458, + 67, 78, 3991, 2475, 2475, 2475, 2886, -1000, -1000, -1000, + -1000, -1000, -1000, 3991, 270, -1000, 3192, -1000, 288, -1000, + 60, -1000, 18, 425, 402, 494, -1000, -1000, -1000, -1000, + 407, -1000, -1000, -1000, -1000, -1000, 233, -1000, 488, 2886, + 340, 1290, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, + 370, 446, 64, 221, -1000, -1000, 439, -1000, 127, -91, + -1000, -1000, 199, -1000, -1000, -1000, -1000, -1000, -1000, 193, + -1000, -1000, -1000, 188, -1000, 387, -1000, 218, -1000, 4033, + 3991, -1000, -1000, 509, 2475, 3659, -1000, -1000, 3535, -1000, + -1000, 1890, 52, 83, -1000, -1000, 184, -1000, -1000, 78, + 78, 312, -1000, -1000, -1000, -1000, 241, 3014, 3014, 3014, + -15, 312, 868, 1007, 672, -1000, 59, 59, 28, 28, + 28, 28, 28, 89, 89, 233, -1000, 233, 2475, 339, + 270, 16, -1000, 2886, -1000, 214, 322, 2475, 121, -1000, + 2886, 233, 253, 253, 253, -1000, 294, 263, -1000, -1000, + 2611, 233, 267, 15, 488, 3991, 2886, 2190, -1000, -1000, + 2886, 369, -1000, -1000, -1000, 476, 78, 1440, -1000, 1440, + 3867, -1000, 210, -1000, -1000, -83, 6, -1000, -1000, -1000, + 248, 291, 261, 4033, -1000, -1000, -14, 502, -1000, 286, + -1000, -1000, 12, -1000, -1000, -1000, -1000, -15, 312, 803, + -1000, 3014, 3014, -1000, -1000, 253, 2475, 2040, 78, 697, + 3014, 319, 62, -1000, 2886, 216, -1000, -1000, -1000, -1000, + 270, -1000, -1000, 377, 3867, 3867, 476, -1000, 78, -1000, + 78, 3867, -1000, 1290, -1000, 242, -1000, 347, -1000, 58, + -1000, -1000, -1000, -1000, -1000, -1000, -1000, 187, -1000, 160, + 1740, 3991, 496, 479, 1590, -1000, 3014, 312, 312, -1000, + 233, -1000, 233, 347, -1000, 347, 348, 347, -42, 347, + -43, 347, 347, 347, 347, 347, 347, -1000, 790, -107, + -1000, 78, 2886, -1000, 270, -1000, 379, 0, -1000, -1000, + 217, -1000, -1000, 3867, -1000, -1000, 443, -1000, 442, 240, + 237, -1000, -1000, -1000, -1000, -1000, 2886, 2886, -1000, 312, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, - -1000, -1000, -1000, 2912, 182, 170, 74, 269, 182, 3760, - 3760, -1000, -1000, -1000, -1000, -1000, -1000, 74, 255, 742, - -1000, -1000, -1000, -1000, -1000, -1000, + -1000, -1000, -1000, -1000, -1000, -1000, 3014, 233, 185, 78, + 263, 233, 3867, 3867, -1000, -1000, -1000, -1000, -1000, -1000, + 78, 251, 403, -1000, -1000, -1000, -1000, -1000, -1000, } var yyPgo = [...]int{ - 0, 663, 16, 405, 661, 655, 652, 650, 649, 648, - 646, 645, 644, 643, 634, 640, 631, 629, 628, 45, - 623, 13, 51, 7, 36, 8, 622, 31, 54, 47, - 620, 34, 618, 616, 44, 614, 53, 609, 607, 40, - 605, 604, 603, 602, 597, 10, 594, 593, 579, 574, - 573, 25, 1, 4, 28, 9, 572, 490, 27, 568, - 21, 566, 559, 553, 551, 12, 550, 43, 549, 11, - 548, 545, 30, 18, 544, 540, 105, 61, 539, 15, - 0, 20, 35, 26, 538, 647, 22, 41, 537, 531, - 530, 527, 526, 525, 524, 517, 515, 513, 152, 512, - 510, 509, 37, 6, 500, 498, 492, 32, 491, 29, - 489, 487, 486, 14, 5, 485, 2, 484, 33, 483, - 478, 472, 119, 3, 470, + 0, 695, 34, 431, 693, 690, 689, 688, 685, 682, + 680, 677, 673, 671, 670, 517, 669, 668, 665, 43, + 661, 14, 56, 9, 25, 76, 660, 20, 55, 48, + 658, 29, 657, 655, 42, 653, 707, 651, 650, 44, + 649, 647, 643, 640, 639, 12, 636, 634, 633, 632, + 629, 16, 1, 4, 8, 7, 628, 112, 45, 626, + 18, 625, 621, 614, 612, 6, 611, 30, 610, 13, + 603, 601, 32, 10, 596, 595, 123, 60, 594, 17, + 0, 22, 33, 28, 593, 652, 21, 37, 592, 590, + 589, 587, 586, 585, 579, 578, 577, 576, 38, 572, + 567, 566, 36, 5, 564, 563, 562, 27, 559, 26, + 557, 555, 554, 15, 11, 553, 2, 552, 31, 550, + 549, 548, 121, 3, 542, 541, } var yyR1 = [...]int{ @@ -960,42 +973,42 @@ var yyR1 = [...]int{ 106, 106, 106, 106, 108, 115, 115, 115, 115, 109, 109, 117, 117, 116, 112, 112, 112, 113, 113, 113, 114, 114, 114, 10, 10, 10, 118, 118, 11, 11, - 13, 13, 13, 13, 13, 119, 119, 14, 14, 124, - 15, 16, 16, 17, 17, 19, 19, 23, 23, 22, - 22, 24, 24, 24, 24, 84, 84, 84, 83, 83, - 26, 27, 27, 28, 28, 29, 29, 29, 29, 38, - 71, 71, 30, 30, 30, 31, 31, 32, 32, 89, - 89, 88, 88, 88, 87, 87, 33, 33, 33, 34, - 34, 35, 35, 37, 37, 36, 36, 39, 39, 25, - 25, 25, 25, 25, 25, 25, 75, 75, 41, 41, - 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, - 50, 50, 50, 50, 50, 50, 42, 42, 42, 42, - 42, 42, 42, 21, 21, 51, 51, 51, 57, 52, - 52, 45, 45, 45, 45, 45, 45, 45, 45, 45, + 13, 13, 13, 13, 13, 119, 119, 14, 14, 14, + 125, 15, 16, 16, 17, 17, 19, 19, 23, 23, + 22, 22, 24, 24, 24, 24, 84, 84, 84, 83, + 83, 26, 27, 27, 28, 28, 29, 29, 29, 29, + 38, 71, 71, 30, 30, 30, 31, 31, 32, 32, + 89, 89, 88, 88, 88, 87, 87, 33, 33, 33, + 34, 34, 35, 35, 37, 37, 36, 36, 39, 39, + 25, 25, 25, 25, 25, 25, 25, 75, 75, 41, + 41, 40, 40, 40, 40, 40, 40, 40, 40, 40, + 40, 50, 50, 50, 50, 50, 50, 42, 42, 42, + 42, 42, 42, 42, 21, 21, 51, 51, 51, 57, + 52, 52, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, - 45, 45, 45, 45, 45, 45, 45, 48, 48, 48, - 46, 46, 46, 46, 46, 46, 47, 47, 47, 49, - 49, 49, 91, 91, 91, 91, 91, 91, 91, 91, - 91, 91, 91, 91, 91, 91, 91, 91, 91, 61, - 61, 20, 20, 59, 59, 60, 62, 62, 58, 58, - 58, 44, 44, 44, 44, 44, 44, 44, 63, 63, - 64, 64, 65, 65, 66, 66, 67, 68, 68, 68, - 69, 69, 69, 69, 43, 43, 43, 43, 43, 43, - 70, 70, 70, 70, 53, 53, 55, 55, 54, 56, - 72, 72, 73, 76, 76, 77, 77, 74, 74, 78, - 78, 78, 81, 81, 82, 82, 85, 85, 86, 86, + 45, 45, 45, 45, 45, 45, 45, 45, 48, 48, + 48, 46, 46, 46, 46, 46, 46, 47, 47, 47, + 49, 49, 49, 91, 91, 91, 91, 91, 91, 91, + 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, + 61, 61, 20, 20, 59, 59, 60, 62, 62, 58, + 58, 58, 44, 44, 44, 44, 44, 44, 44, 63, + 63, 64, 64, 65, 65, 66, 66, 67, 68, 68, + 68, 69, 69, 69, 69, 43, 43, 43, 43, 43, + 43, 70, 70, 70, 70, 53, 53, 55, 55, 54, + 56, 72, 72, 73, 76, 76, 77, 77, 74, 74, + 78, 78, 78, 81, 81, 82, 82, 85, 85, 86, + 86, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, - 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, - 79, 80, 80, 80, 80, 80, 80, 80, 80, 80, + 79, 79, 79, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, - 80, 80, 80, 80, 80, 80, 80, 122, 123, 90, - 90, 90, + 80, 80, 80, 80, 80, 80, 80, 80, 80, 122, + 123, 124, 90, 90, 90, } var yyR2 = [...]int{ @@ -1011,29 +1024,30 @@ var yyR2 = [...]int{ 2, 1, 2, 1, 4, 2, 3, 2, 2, 1, 1, 1, 3, 2, 0, 1, 3, 1, 2, 3, 1, 1, 1, 6, 6, 8, 0, 1, 4, 4, - 4, 5, 3, 3, 5, 0, 1, 2, 2, 0, - 2, 0, 2, 1, 2, 0, 1, 0, 1, 1, - 3, 1, 2, 3, 5, 0, 1, 2, 1, 1, - 2, 1, 3, 1, 1, 1, 1, 3, 3, 2, - 1, 3, 4, 4, 3, 2, 4, 0, 1, 0, - 1, 0, 1, 2, 1, 1, 1, 2, 2, 2, - 3, 2, 2, 2, 1, 1, 3, 0, 2, 1, - 3, 3, 2, 3, 1, 2, 0, 3, 1, 1, - 3, 3, 4, 4, 5, 3, 4, 5, 6, 2, - 1, 2, 1, 2, 1, 2, 1, 1, 1, 1, - 1, 1, 1, 0, 2, 1, 1, 1, 3, 1, - 3, 1, 1, 1, 1, 1, 3, 3, 3, 3, - 3, 3, 3, 3, 3, 3, 3, 3, 2, 2, - 2, 2, 3, 1, 1, 1, 1, 4, 5, 6, - 6, 6, 8, 7, 5, 4, 1, 1, 1, 4, - 4, 4, 2, 1, 2, 2, 2, 1, 2, 2, - 1, 2, 2, 2, 2, 2, 2, 2, 1, 0, - 1, 0, 2, 1, 2, 4, 0, 2, 1, 3, - 5, 1, 1, 1, 1, 1, 1, 1, 0, 3, - 0, 2, 0, 3, 1, 3, 2, 0, 1, 1, - 0, 2, 4, 4, 2, 1, 3, 5, 4, 6, - 1, 3, 3, 5, 1, 3, 1, 2, 3, 1, - 1, 3, 3, 0, 2, 0, 3, 0, 1, 0, + 4, 5, 3, 3, 5, 0, 1, 2, 2, 2, + 0, 2, 0, 2, 1, 2, 0, 1, 0, 1, + 1, 3, 1, 2, 3, 5, 0, 1, 2, 1, + 1, 2, 1, 3, 1, 1, 1, 1, 3, 3, + 2, 1, 3, 4, 4, 3, 2, 4, 0, 1, + 0, 1, 0, 1, 2, 1, 1, 1, 2, 2, + 2, 3, 2, 2, 2, 1, 1, 3, 0, 2, + 1, 3, 3, 2, 3, 1, 2, 0, 3, 1, + 1, 3, 3, 4, 4, 5, 3, 4, 5, 6, + 2, 1, 2, 1, 2, 1, 2, 1, 1, 1, + 1, 1, 1, 1, 0, 2, 1, 1, 1, 3, + 1, 3, 1, 1, 1, 1, 1, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 2, + 2, 2, 2, 3, 1, 1, 1, 1, 4, 5, + 6, 6, 6, 8, 7, 5, 4, 1, 1, 1, + 4, 4, 4, 2, 1, 2, 2, 2, 1, 2, + 2, 1, 2, 2, 2, 2, 2, 2, 2, 1, + 0, 1, 0, 2, 1, 2, 4, 0, 2, 1, + 3, 5, 1, 1, 1, 1, 1, 1, 1, 0, + 3, 0, 2, 0, 3, 1, 3, 2, 0, 1, + 1, 0, 2, 4, 4, 2, 1, 3, 5, 4, + 6, 1, 3, 3, 5, 1, 3, 1, 2, 3, + 1, 1, 3, 3, 0, 2, 0, 3, 0, 1, + 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, @@ -1045,162 +1059,161 @@ var yyR2 = [...]int{ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, - 1, 1, + 1, 0, 0, 1, 1, } var yyChk = [...]int{ -1000, -120, -1, -2, -6, -7, -8, -9, -10, -11, -13, -14, -3, -4, -18, 8, 9, -12, 93, 94, - 95, 108, 23, 109, 6, -122, 7, 148, 39, -121, - 154, -65, 14, -17, 5, -15, -124, -15, -15, -110, - 39, -78, 98, 103, -81, 42, -80, 117, 136, 130, - 128, 110, 126, 122, 120, 121, 107, 115, 116, 29, - 139, 135, 138, 114, 134, 131, 123, 17, 104, 119, - 141, 113, 132, 124, 125, 137, 112, 133, 142, 153, - 129, 127, 143, 98, 98, 99, 93, 99, 98, -119, - 54, -36, -85, 42, -80, -36, -15, -2, 56, -69, - 16, 15, -5, -3, -122, 18, -74, 101, -16, -27, - -28, -29, -30, -38, -57, -122, -36, 10, -111, -107, - 42, 99, -77, 102, -36, -76, 102, -76, 98, 10, - -36, 144, 55, 92, -19, 19, -123, 41, 148, -25, - -40, 58, -45, 26, 21, -44, -41, -58, -56, -57, - 81, 82, 89, 59, 91, -48, -46, -47, -49, 44, - 43, 45, 46, 47, 48, 51, 52, 53, -81, -85, - -54, -122, 149, 150, 151, 61, 28, 145, 146, 147, - 102, 87, 148, 42, -80, -66, -67, -25, -65, -2, - -37, 24, -36, 50, 27, 40, -33, -34, -35, 30, - 33, 35, 31, 36, -89, 20, -27, -2, -122, -88, - -87, 20, -85, 44, -36, 41, 40, -92, -95, -97, - -96, -93, -94, 128, 129, 132, 133, 134, 135, 136, - 137, 138, 139, 110, 124, 125, 126, 127, 112, 113, - 114, 115, 116, 117, 119, 120, 121, 122, 123, -77, - -36, 58, 97, 96, -36, 21, -36, -36, 98, 10, - -86, -85, -79, 97, 57, 20, 22, 140, 60, 15, - 61, 93, 35, 146, 147, 145, 150, 26, 9, 23, - 109, 19, 85, 95, 64, 65, 111, 21, 53, 10, - 12, 13, 102, 101, 75, 99, 33, 7, 91, 24, - 72, 30, 25, 31, 73, 16, 87, 36, 58, 51, - 37, 56, 14, 34, 74, 96, 148, 32, 6, 152, - 27, 108, 98, 144, 63, 100, 52, 5, 103, 8, - 38, 28, 62, 11, -22, -24, 83, -25, -85, 57, - 56, 72, 40, 17, -25, -42, 75, 58, 73, 74, - 60, 78, 77, 88, 81, 82, 83, 84, 85, 86, - 87, 79, 80, 66, 67, 68, 69, 70, 71, 76, - -75, -122, -57, -122, -45, -45, -45, -45, -45, -122, - 92, -52, -25, -122, -122, -122, -122, -61, -25, -122, - -122, -122, -122, 40, -68, 22, 23, -69, -123, -43, - 27, 28, -2, -122, -36, -72, -73, -58, -81, -85, - -28, -29, -28, -29, 30, 30, 30, 34, 30, -34, - -85, -123, -123, -2, -87, -39, 11, -112, -113, -114, - -82, 44, 45, -81, -79, -107, -108, -115, 104, 103, - -109, 99, 25, -104, 51, 58, -100, 142, -98, 39, - -98, -98, -98, -98, -98, -102, 39, -102, -102, -103, - 39, -103, 42, 21, -118, 105, 100, -118, -36, -36, - -26, 40, 10, -84, -83, 20, -81, 44, 92, -25, - -25, -50, 51, 58, 52, 53, -25, -25, -45, -51, - -54, -57, 49, 75, 73, 74, 60, -45, -45, -45, + 95, 108, 23, 109, 112, 6, -122, 7, 149, 39, + -121, 155, -65, 14, -17, 5, -15, -125, -15, -15, + -110, 39, -78, 98, 103, -81, 42, -80, 118, 137, + 131, 129, 110, 127, 123, 121, 122, 107, 116, 117, + 29, 140, 136, 139, 115, 135, 132, 124, 17, 104, + 120, 142, 114, 133, 125, 126, 138, 113, 134, 143, + 154, 130, 128, 144, 98, 98, 99, 93, 99, 98, + -119, 54, -36, -85, 42, -80, -36, -124, -15, -2, + 56, -69, 16, 15, -5, -3, -122, 18, -74, 101, + -16, -27, -28, -29, -30, -38, -57, -122, -36, 10, + -111, -107, 42, 99, -77, 102, -36, -76, 102, -76, + 98, 10, -36, 145, 55, 92, -19, 19, -123, 41, + 149, -25, -40, 58, -45, 26, 21, -44, -41, -58, + -56, -57, 81, 82, 89, 59, 91, -48, -46, -47, + -49, 44, 43, 45, 46, 47, 48, 51, 52, 53, + -81, -85, -54, -122, 150, 151, 152, 61, 28, 146, + 147, 148, 102, 87, 149, 42, -80, -66, -67, -25, + -65, -2, -37, 24, -36, 50, 27, 40, -33, -34, + -35, 30, 33, 35, 31, 36, -89, 20, -27, -2, + -122, -88, -87, 20, -85, 44, -36, 41, 40, -92, + -95, -97, -96, -93, -94, 129, 130, 133, 134, 135, + 136, 137, 138, 139, 140, 110, 125, 126, 127, 128, + 113, 114, 115, 116, 117, 118, 120, 121, 122, 123, + 124, -77, -36, 58, 97, 96, -36, 21, -36, -36, + 98, 10, -86, -85, -79, 97, 57, 20, 22, 141, + 60, 15, 61, 93, 35, 147, 148, 146, 151, 26, + 9, 23, 109, 19, 85, 95, 64, 65, 111, 21, + 53, 10, 12, 13, 102, 101, 75, 99, 33, 7, + 91, 24, 72, 30, 25, 31, 73, 16, 87, 36, + 58, 51, 37, 56, 14, 34, 74, 96, 149, 32, + 6, 153, 27, 108, 98, 145, 63, 100, 52, 5, + 103, 8, 38, 28, 62, 11, 112, -22, -24, 83, + -25, -85, 57, 56, 72, 40, 17, -25, -42, 75, + 58, 73, 74, 60, 78, 77, 88, 81, 82, 83, + 84, 85, 86, 87, 79, 80, 66, 67, 68, 69, + 70, 71, 76, -75, -122, -57, -122, -45, -45, -45, + -45, -45, -122, 92, -52, -25, -122, -122, -122, -122, + -61, -25, -122, -122, -122, -122, 40, -68, 22, 23, + -69, -123, -43, 27, 28, -2, -122, -36, -72, -73, + -58, -81, -85, -28, -29, -28, -29, 30, 30, 30, + 34, 30, -34, -85, -123, -123, -2, -87, -39, 11, + -112, -113, -114, -82, 44, 45, -81, -79, -107, -108, + -115, 104, 103, -109, 99, 25, -104, 51, 58, -100, + 143, -98, 39, -98, -98, -98, -98, -98, -102, 39, + -102, -102, -103, 39, -103, 42, 21, -118, 105, 100, + -118, -36, -36, -26, 40, 10, -84, -83, 20, -81, + 44, 92, -25, -25, -50, 51, 58, 52, 53, -25, + -25, -45, -51, -54, -57, 49, 75, 73, 74, 60, -45, -45, -45, -45, -45, -45, -45, -45, -45, -45, - -45, -45, 42, -81, -23, 19, -22, -82, -86, -79, - 40, -123, -25, -58, -19, -59, -60, 62, -58, -22, - -22, -22, -67, -72, -53, -55, -54, -122, -2, -70, - -81, -39, 40, 66, 92, -32, -31, 37, 38, -31, - 30, -123, -65, -25, 40, -114, 66, 39, 25, -109, - 42, 42, -99, 26, 51, -101, 143, 45, 45, 45, - 37, -107, -36, -58, -39, -24, -27, -83, 83, -86, - 51, 52, 53, -51, -45, -45, -45, -21, 111, 57, - -123, -123, -22, -122, 92, -25, 20, 40, -22, -62, - -60, 64, -25, -123, -123, -123, -123, 40, -123, -123, - -123, 40, 92, -65, -73, -25, -82, -25, 39, -69, - -113, -114, -117, -116, -81, 42, -105, 140, 44, 45, - 46, 51, 145, 41, 40, 41, 40, -36, 100, -63, - 12, 92, -21, 57, -45, -45, -123, -23, -82, -91, - 128, 110, 126, 122, 131, 141, 124, 142, 125, 115, - 112, 113, 114, 117, 44, -45, -65, 65, -25, 63, - -55, 28, -2, -122, -81, -81, -69, -71, -81, 41, - 40, -98, -106, 104, 25, 103, 45, 45, -90, -122, - -82, -58, -64, 13, 15, 83, -45, -123, -123, -98, - -98, -103, -98, 116, -98, 116, -98, -98, -98, -98, - -98, -98, -123, 40, -20, 152, -25, -53, -2, 92, - 40, 41, -116, 25, 25, 41, 41, -25, -52, -45, - -123, 44, -123, -81, -81, -123, + -45, -45, -45, -45, -45, 42, -81, -23, 19, -22, + -82, -86, -79, 40, -123, -25, -58, -19, -59, -60, + 62, -58, -22, -22, -22, -67, -72, -53, -55, -54, + -122, -2, -70, -81, -39, 40, 66, 92, -32, -31, + 37, 38, -31, 30, -123, -65, -25, 40, -114, 66, + 39, 25, -109, 42, 42, -99, 26, 51, -101, 144, + 45, 45, 45, 37, -107, -36, -58, -39, -24, -27, + -83, 83, -86, 51, 52, 53, -51, -45, -45, -45, + -21, 111, 57, -123, -123, -22, -122, 92, -25, 20, + 40, -22, -62, -60, 64, -25, -123, -123, -123, -123, + 40, -123, -123, -123, 40, 92, -65, -73, -25, -82, + -25, 39, -69, -113, -114, -117, -116, -81, 42, -105, + 141, 44, 45, 46, 51, 146, 41, 40, 41, 40, + -36, 100, -63, 12, 92, -21, 57, -45, -45, -123, + -23, -82, -91, 129, 110, 127, 123, 132, 142, 125, + 143, 126, 116, 113, 114, 115, 118, 44, -45, -65, + 65, -25, 63, -55, 28, -2, -122, -81, -81, -69, + -71, -81, 41, 40, -98, -106, 104, 25, 103, 45, + 45, -90, -122, -82, -58, -64, 13, 15, 83, -45, + -123, -123, -98, -98, -103, -98, 117, -98, 117, -98, + -98, -98, -98, -98, -98, -123, 40, -20, 153, -25, + -53, -2, 92, 40, 41, -116, 25, 25, 41, 41, + -25, -52, -45, -123, 44, -123, -81, -81, -123, } var yyDef = [...]int{ 0, -2, 2, -2, 5, 6, 7, 8, 9, 10, - 11, 12, 312, 0, 129, 129, 129, 0, 349, 0, - 0, 125, 0, 0, 129, 0, 23, 24, 467, 1, - 3, 320, 0, 0, 133, 347, 131, 0, 0, 28, - 0, 0, 345, 350, 351, 352, 353, 431, 432, 433, - 434, 435, 436, 437, 438, 439, 440, 441, 442, 443, - 444, 445, 446, 447, 448, 449, 450, 451, 452, 453, - 454, 455, 456, 457, 458, 459, 460, 461, 462, 463, - 464, 465, 466, 0, 343, 343, 0, 0, 0, 0, - 126, 127, 185, 356, 357, 128, 135, 17, 0, 13, - 0, 0, 312, 19, 0, 134, 0, 348, 130, 0, - 151, 153, 154, 155, -2, 0, 171, 0, 0, 32, - 0, 345, 0, 0, 0, 0, 0, 0, 0, 0, - 122, 123, 0, 0, 0, 136, 18, 468, 25, 321, - 189, 0, 194, 196, 0, 231, 232, 233, 234, 235, - 0, 0, 0, 0, 0, 253, 254, 255, 256, 301, - 302, 303, 304, 305, 306, 307, 198, 199, 298, 0, - 339, 0, 0, 0, 0, 289, 0, 266, 267, 268, - 0, 0, 0, -2, -2, 313, 314, 317, 320, 17, - 0, 0, 184, 132, 0, 0, 0, 0, 0, 176, - 0, 0, 0, 0, 0, 170, 0, 17, 0, 159, - 172, 0, 174, 175, 187, 104, 0, 78, 74, 37, - 38, 67, 40, 67, 67, 59, 60, 61, 62, 63, - 64, 65, 66, 52, 67, 67, 67, 56, 41, 42, - 43, 44, 45, 46, 69, 69, 69, 71, 71, 0, - 30, 0, 116, 116, 118, 344, 119, 120, 0, 0, - 186, 358, 359, 360, 361, 362, 363, 364, 365, 366, - 367, 368, 369, 370, 371, 372, 373, 374, 375, 376, - 377, 378, 379, 380, 381, 382, 383, 384, 385, 386, - 387, 388, 389, 390, 391, 392, 393, 394, 395, 396, - 397, 398, 399, 400, 401, 402, 403, 404, 405, 406, - 407, 408, 409, 410, 411, 412, 413, 414, 415, 416, - 417, 418, 419, 420, 421, 422, 423, 424, 425, 426, - 427, 428, 429, 430, 16, 139, 141, 145, 0, 0, - 0, 0, 0, 0, 192, 0, 0, 0, 0, 0, + 11, 12, 313, 0, 130, 130, 130, 0, 350, 0, + 0, 125, 0, 0, 471, 130, 0, 23, 24, 469, + 1, 3, 321, 0, 0, 134, 348, 132, 0, 0, + 28, 0, 0, 346, 351, 352, 353, 354, 433, 434, + 435, 436, 437, 438, 439, 440, 441, 442, 443, 444, + 445, 446, 447, 448, 449, 450, 451, 452, 453, 454, + 455, 456, 457, 458, 459, 460, 461, 462, 463, 464, + 465, 466, 467, 468, 0, 344, 344, 0, 0, 0, + 0, 126, 127, 186, 357, 358, 128, 129, 136, 17, + 0, 13, 0, 0, 313, 19, 0, 135, 0, 349, + 131, 0, 152, 154, 155, 156, -2, 0, 172, 0, + 0, 32, 0, 346, 0, 0, 0, 0, 0, 0, + 0, 0, 122, 123, 0, 0, 0, 137, 18, 470, + 25, 322, 190, 0, 195, 197, 0, 232, 233, 234, + 235, 236, 0, 0, 0, 0, 0, 254, 255, 256, + 257, 302, 303, 304, 305, 306, 307, 308, 199, 200, + 299, 0, 340, 0, 0, 0, 0, 290, 0, 267, + 268, 269, 0, 0, 0, -2, -2, 314, 315, 318, + 321, 17, 0, 0, 185, 133, 0, 0, 0, 0, + 0, 177, 0, 0, 0, 0, 0, 171, 0, 17, + 0, 160, 173, 0, 175, 176, 188, 104, 0, 78, + 74, 37, 38, 67, 40, 67, 67, 59, 60, 61, + 62, 63, 64, 65, 66, 52, 67, 67, 67, 56, + 41, 42, 43, 44, 45, 46, 69, 69, 69, 71, + 71, 0, 30, 0, 116, 116, 118, 345, 119, 120, + 0, 0, 187, 359, 360, 361, 362, 363, 364, 365, + 366, 367, 368, 369, 370, 371, 372, 373, 374, 375, + 376, 377, 378, 379, 380, 381, 382, 383, 384, 385, + 386, 387, 388, 389, 390, 391, 392, 393, 394, 395, + 396, 397, 398, 399, 400, 401, 402, 403, 404, 405, + 406, 407, 408, 409, 410, 411, 412, 413, 414, 415, + 416, 417, 418, 419, 420, 421, 422, 423, 424, 425, + 426, 427, 428, 429, 430, 431, 432, 16, 140, 142, + 146, 0, 0, 0, 0, 0, 0, 193, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 216, 217, 218, 219, 220, 221, 222, - 195, 0, 209, 0, 248, 249, 250, 251, 0, 137, - 0, 0, 229, 0, 0, 0, 135, 0, 290, 0, - 0, 0, 0, 0, 316, 318, 319, 14, 20, 21, - 0, 0, -2, 0, 183, 187, 340, 0, 298, 0, - 152, 167, 0, 164, 177, 178, 179, 0, 181, 182, - 157, 158, 228, 17, 173, 312, 0, 31, 105, 107, - 110, 111, 112, 354, 355, 33, 34, 0, 0, 0, - 0, 99, 100, 81, 79, 0, 76, 75, 39, 0, - 57, 58, 53, 54, 55, 47, 0, 48, 49, 50, - 0, 51, 0, 346, 0, 117, 0, 0, 121, 124, - 187, 0, 0, 142, 146, 0, 148, 149, 0, 190, - 191, 193, 210, 0, 212, 214, 322, 323, 200, 201, - 225, 226, 227, 0, 0, 0, 0, 223, 205, 0, - 236, 237, 238, 239, 240, 241, 242, 243, 244, 245, - 246, 247, 0, 252, 0, 0, 138, 299, 0, -2, - 0, 338, 0, 0, 0, 296, 293, 0, 0, 0, - 0, 0, 315, 22, 324, 334, 336, 0, 17, 0, - 330, 312, 0, 0, 0, 162, 168, 0, 0, 163, - 180, -2, 320, 188, 0, 108, 0, 0, 95, 0, - 97, 98, 87, 0, 80, 36, 77, 0, 0, 0, - 0, 113, 114, 0, 308, 140, 150, 147, 143, 0, - 211, 213, 215, 202, 223, 206, 0, 203, 0, 0, - 197, 257, 0, 137, 0, 230, 0, 0, 312, 0, - 294, 0, 0, 265, 269, 270, 271, 0, 337, -2, - 0, 0, 0, 320, 341, 342, 299, 165, 0, 27, - 106, 109, 0, 101, 67, 96, 89, 88, 82, 83, - 84, 85, 86, 68, 0, 72, 0, 469, 0, 310, - 0, 0, 204, 0, 224, 207, 258, 0, 300, 0, - 67, 273, 67, 71, 67, 277, 67, 280, 67, 67, - 67, 67, 67, 67, 288, 0, 291, 264, 297, 0, - 335, 0, -2, 0, 332, 331, 26, 0, 160, 94, - 0, 103, 35, 0, 91, 93, 0, 0, 29, 470, - 471, 115, 15, 0, 0, 144, 208, 259, 260, 272, - 274, 275, 276, 278, 279, 281, 282, 283, 284, 285, - 286, 287, 261, 0, 0, 0, 295, 327, 17, 0, - 0, 166, 102, 90, 92, 70, 73, 311, 309, 0, - 263, 292, -2, 333, 161, 262, + 0, 0, 0, 0, 0, 0, 217, 218, 219, 220, + 221, 222, 223, 196, 0, 210, 0, 249, 250, 251, + 252, 0, 138, 0, 0, 230, 0, 0, 0, 136, + 0, 291, 0, 0, 0, 0, 0, 317, 319, 320, + 14, 20, 21, 0, 0, -2, 0, 184, 188, 341, + 0, 299, 0, 153, 168, 0, 165, 178, 179, 180, + 0, 182, 183, 158, 159, 229, 17, 174, 313, 0, + 31, 105, 107, 110, 111, 112, 355, 356, 33, 34, + 0, 0, 0, 0, 99, 100, 81, 79, 0, 76, + 75, 39, 0, 57, 58, 53, 54, 55, 47, 0, + 48, 49, 50, 0, 51, 0, 347, 0, 117, 0, + 0, 121, 124, 188, 0, 0, 143, 147, 0, 149, + 150, 0, 191, 192, 194, 211, 0, 213, 215, 323, + 324, 201, 202, 226, 227, 228, 0, 0, 0, 0, + 224, 206, 0, 237, 238, 239, 240, 241, 242, 243, + 244, 245, 246, 247, 248, 0, 253, 0, 0, 139, + 300, 0, -2, 0, 339, 0, 0, 0, 297, 294, + 0, 0, 0, 0, 0, 316, 22, 325, 335, 337, + 0, 17, 0, 331, 313, 0, 0, 0, 163, 169, + 0, 0, 164, 181, -2, 321, 189, 0, 108, 0, + 0, 95, 0, 97, 98, 87, 0, 80, 36, 77, + 0, 0, 0, 0, 113, 114, 0, 309, 141, 151, + 148, 144, 0, 212, 214, 216, 203, 224, 207, 0, + 204, 0, 0, 198, 258, 0, 138, 0, 231, 0, + 0, 313, 0, 295, 0, 0, 266, 270, 271, 272, + 0, 338, -2, 0, 0, 0, 321, 342, 343, 300, + 166, 0, 27, 106, 109, 0, 101, 67, 96, 89, + 88, 82, 83, 84, 85, 86, 68, 0, 72, 0, + 472, 0, 311, 0, 0, 205, 0, 225, 208, 259, + 0, 301, 0, 67, 274, 67, 71, 67, 278, 67, + 281, 67, 67, 67, 67, 67, 67, 289, 0, 292, + 265, 298, 0, 336, 0, -2, 0, 333, 332, 26, + 0, 161, 94, 0, 103, 35, 0, 91, 93, 0, + 0, 29, 473, 474, 115, 15, 0, 0, 145, 209, + 260, 261, 273, 275, 276, 277, 279, 280, 282, 283, + 284, 285, 286, 287, 288, 262, 0, 0, 0, 296, + 328, 17, 0, 0, 167, 102, 90, 92, 70, 73, + 312, 310, 0, 264, 293, -2, 334, 162, 263, } var yyTok1 = [...]int{ @@ -1209,7 +1222,7 @@ var yyTok1 = [...]int{ 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 59, 3, 3, 3, 86, 78, 3, 39, 41, 83, 81, 40, 82, 92, 84, 3, 3, - 3, 3, 3, 3, 3, 3, 3, 3, 3, 154, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 155, 67, 66, 68, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, @@ -1233,7 +1246,7 @@ var yyTok2 = [...]int{ 119, 120, 121, 122, 123, 124, 125, 126, 127, 128, 129, 130, 131, 132, 133, 134, 135, 136, 137, 138, 139, 140, 141, 142, 143, 144, 145, 146, 147, 148, - 149, 150, 151, 152, 153, + 149, 150, 151, 152, 153, 154, } var yyTok3 = [...]int{ 0, @@ -2331,677 +2344,677 @@ yydefault: yyVAL.statement = &Show{Type: "table", OnTable: yyDollar[2].tableName} } case 129: + yyDollar = yyS[yypt-2 : yypt+1] + //line sql.y:849 + { + yyVAL.statement = &Explain{} + } + case 130: yyDollar = yyS[yypt-0 : yypt+1] - //line sql.y:850 + //line sql.y:854 { setAllowComments(yylex, true) } - case 130: + case 131: yyDollar = yyS[yypt-2 : yypt+1] - //line sql.y:854 + //line sql.y:858 { yyVAL.bytes2 = yyDollar[2].bytes2 setAllowComments(yylex, false) } - case 131: + case 132: yyDollar = yyS[yypt-0 : yypt+1] - //line sql.y:860 + //line sql.y:864 { yyVAL.bytes2 = nil } - case 132: + case 133: yyDollar = yyS[yypt-2 : yypt+1] - //line sql.y:864 + //line sql.y:868 { yyVAL.bytes2 = append(yyDollar[1].bytes2, yyDollar[2].bytes) } - case 133: + case 134: yyDollar = yyS[yypt-1 : yypt+1] - //line sql.y:870 + //line sql.y:874 { yyVAL.str = UnionStr } - case 134: + case 135: yyDollar = yyS[yypt-2 : yypt+1] - //line sql.y:874 + //line sql.y:878 { yyVAL.str = UnionAllStr } - case 135: + case 136: yyDollar = yyS[yypt-0 : yypt+1] - //line sql.y:879 + //line sql.y:883 { yyVAL.str = "" } - case 136: + case 137: yyDollar = yyS[yypt-1 : yypt+1] - //line sql.y:883 + //line sql.y:887 { yyVAL.str = DistinctStr } - case 137: + case 138: yyDollar = yyS[yypt-0 : yypt+1] - //line sql.y:888 + //line sql.y:892 { yyVAL.selectExprs = nil } - case 138: + case 139: yyDollar = yyS[yypt-1 : yypt+1] - //line sql.y:892 + //line sql.y:896 { yyVAL.selectExprs = yyDollar[1].selectExprs } - case 139: + case 140: yyDollar = yyS[yypt-1 : yypt+1] - //line sql.y:898 + //line sql.y:902 { yyVAL.selectExprs = SelectExprs{yyDollar[1].selectExpr} } - case 140: + case 141: yyDollar = yyS[yypt-3 : yypt+1] - //line sql.y:902 + //line sql.y:906 { yyVAL.selectExprs = append(yyVAL.selectExprs, yyDollar[3].selectExpr) } - case 141: + case 142: yyDollar = yyS[yypt-1 : yypt+1] - //line sql.y:908 + //line sql.y:912 { yyVAL.selectExpr = &StarExpr{} } - case 142: + case 143: yyDollar = yyS[yypt-2 : yypt+1] - //line sql.y:912 + //line sql.y:916 { yyVAL.selectExpr = &AliasedExpr{Expr: yyDollar[1].expr, As: yyDollar[2].colIdent} } - case 143: + case 144: yyDollar = yyS[yypt-3 : yypt+1] - //line sql.y:916 + //line sql.y:920 { yyVAL.selectExpr = &StarExpr{TableName: TableName{Name: yyDollar[1].tableIdent}} } - case 144: + case 145: yyDollar = yyS[yypt-5 : yypt+1] - //line sql.y:920 + //line sql.y:924 { yyVAL.selectExpr = &StarExpr{TableName: TableName{Qualifier: yyDollar[1].tableIdent, Name: yyDollar[3].tableIdent}} } - case 145: + case 146: yyDollar = yyS[yypt-0 : yypt+1] - //line sql.y:925 + //line sql.y:929 { yyVAL.colIdent = ColIdent{} } - case 146: + case 147: yyDollar = yyS[yypt-1 : yypt+1] - //line sql.y:929 + //line sql.y:933 { yyVAL.colIdent = yyDollar[1].colIdent } - case 147: + case 148: yyDollar = yyS[yypt-2 : yypt+1] - //line sql.y:933 + //line sql.y:937 { yyVAL.colIdent = yyDollar[2].colIdent } - case 149: + case 150: yyDollar = yyS[yypt-1 : yypt+1] - //line sql.y:940 + //line sql.y:944 { yyVAL.colIdent = NewColIdent(string(yyDollar[1].bytes)) } - case 150: + case 151: yyDollar = yyS[yypt-2 : yypt+1] - //line sql.y:946 + //line sql.y:950 { yyVAL.tableExprs = yyDollar[2].tableExprs } - case 151: + case 152: yyDollar = yyS[yypt-1 : yypt+1] - //line sql.y:952 + //line sql.y:956 { yyVAL.tableExprs = TableExprs{yyDollar[1].tableExpr} } - case 152: + case 153: yyDollar = yyS[yypt-3 : yypt+1] - //line sql.y:956 + //line sql.y:960 { yyVAL.tableExprs = append(yyVAL.tableExprs, yyDollar[3].tableExpr) } - case 155: + case 156: yyDollar = yyS[yypt-1 : yypt+1] - //line sql.y:966 + //line sql.y:970 { yyVAL.tableExpr = yyDollar[1].aliasedTableName } - case 156: + case 157: yyDollar = yyS[yypt-1 : yypt+1] - //line sql.y:970 + //line sql.y:974 { yyVAL.tableExpr = &AliasedTableExpr{Expr: yyDollar[1].subquery} } - case 157: + case 158: yyDollar = yyS[yypt-3 : yypt+1] - //line sql.y:974 + //line sql.y:978 { yyVAL.tableExpr = &AliasedTableExpr{Expr: yyDollar[1].subquery, As: yyDollar[3].tableIdent} } - case 158: + case 159: yyDollar = yyS[yypt-3 : yypt+1] - //line sql.y:978 + //line sql.y:982 { yyVAL.tableExpr = &ParenTableExpr{Exprs: yyDollar[2].tableExprs} } - case 159: + case 160: yyDollar = yyS[yypt-2 : yypt+1] - //line sql.y:984 + //line sql.y:988 { yyVAL.aliasedTableName = &AliasedTableExpr{Expr: yyDollar[1].tableName, As: yyDollar[2].tableIdent} } - case 160: + case 161: yyDollar = yyS[yypt-1 : yypt+1] - //line sql.y:990 + //line sql.y:994 { yyVAL.columns = Columns{yyDollar[1].colIdent} } - case 161: + case 162: yyDollar = yyS[yypt-3 : yypt+1] - //line sql.y:994 + //line sql.y:998 { yyVAL.columns = append(yyVAL.columns, yyDollar[3].colIdent) } - case 162: + case 163: yyDollar = yyS[yypt-4 : yypt+1] - //line sql.y:1000 + //line sql.y:1004 { yyVAL.tableExpr = &JoinTableExpr{LeftExpr: yyDollar[1].tableExpr, Join: yyDollar[2].str, RightExpr: yyDollar[3].tableExpr, Condition: yyDollar[4].joinCondition} } - case 163: + case 164: yyDollar = yyS[yypt-4 : yypt+1] - //line sql.y:1004 + //line sql.y:1008 { yyVAL.tableExpr = &JoinTableExpr{LeftExpr: yyDollar[1].tableExpr, Join: yyDollar[2].str, RightExpr: yyDollar[3].tableExpr, Condition: yyDollar[4].joinCondition} } - case 164: + case 165: yyDollar = yyS[yypt-3 : yypt+1] - //line sql.y:1008 + //line sql.y:1012 { yyVAL.tableExpr = &JoinTableExpr{LeftExpr: yyDollar[1].tableExpr, Join: yyDollar[2].str, RightExpr: yyDollar[3].tableExpr} } - case 165: + case 166: yyDollar = yyS[yypt-2 : yypt+1] - //line sql.y:1014 + //line sql.y:1018 { yyVAL.joinCondition = JoinCondition{On: yyDollar[2].expr} } - case 166: + case 167: yyDollar = yyS[yypt-4 : yypt+1] - //line sql.y:1016 + //line sql.y:1020 { yyVAL.joinCondition = JoinCondition{Using: yyDollar[3].columns} } - case 167: + case 168: yyDollar = yyS[yypt-0 : yypt+1] - //line sql.y:1020 + //line sql.y:1024 { yyVAL.joinCondition = JoinCondition{} } - case 168: + case 169: yyDollar = yyS[yypt-1 : yypt+1] - //line sql.y:1022 + //line sql.y:1026 { yyVAL.joinCondition = yyDollar[1].joinCondition } - case 169: + case 170: yyDollar = yyS[yypt-0 : yypt+1] - //line sql.y:1025 + //line sql.y:1029 { yyVAL.empty = struct{}{} } - case 170: + case 171: yyDollar = yyS[yypt-1 : yypt+1] - //line sql.y:1027 + //line sql.y:1031 { yyVAL.empty = struct{}{} } - case 171: + case 172: yyDollar = yyS[yypt-0 : yypt+1] - //line sql.y:1030 + //line sql.y:1034 { yyVAL.tableIdent = NewTableIdent("") } - case 172: + case 173: yyDollar = yyS[yypt-1 : yypt+1] - //line sql.y:1034 + //line sql.y:1038 { yyVAL.tableIdent = yyDollar[1].tableIdent } - case 173: + case 174: yyDollar = yyS[yypt-2 : yypt+1] - //line sql.y:1038 + //line sql.y:1042 { yyVAL.tableIdent = yyDollar[2].tableIdent } - case 175: + case 176: yyDollar = yyS[yypt-1 : yypt+1] - //line sql.y:1045 + //line sql.y:1049 { yyVAL.tableIdent = NewTableIdent(string(yyDollar[1].bytes)) } - case 176: + case 177: yyDollar = yyS[yypt-1 : yypt+1] - //line sql.y:1051 + //line sql.y:1055 { yyVAL.str = JoinStr } - case 177: + case 178: yyDollar = yyS[yypt-2 : yypt+1] - //line sql.y:1055 + //line sql.y:1059 { yyVAL.str = InnerJoinStr } - case 178: + case 179: yyDollar = yyS[yypt-2 : yypt+1] - //line sql.y:1059 + //line sql.y:1063 { yyVAL.str = CrossJoinStr } - case 179: + case 180: yyDollar = yyS[yypt-2 : yypt+1] - //line sql.y:1065 + //line sql.y:1069 { yyVAL.str = LeftJoinStr } - case 180: + case 181: yyDollar = yyS[yypt-3 : yypt+1] - //line sql.y:1069 + //line sql.y:1073 { yyVAL.str = LeftJoinStr } - case 181: + case 182: yyDollar = yyS[yypt-2 : yypt+1] - //line sql.y:1075 + //line sql.y:1079 { yyVAL.str = NaturalJoinStr } - case 182: + case 183: yyDollar = yyS[yypt-2 : yypt+1] - //line sql.y:1079 + //line sql.y:1083 { yyVAL.str = NaturalLeftJoinStr } - case 183: + case 184: yyDollar = yyS[yypt-2 : yypt+1] - //line sql.y:1085 + //line sql.y:1089 { yyVAL.tableName = yyDollar[2].tableName } - case 184: + case 185: yyDollar = yyS[yypt-1 : yypt+1] - //line sql.y:1089 + //line sql.y:1093 { yyVAL.tableName = yyDollar[1].tableName } - case 185: + case 186: yyDollar = yyS[yypt-1 : yypt+1] - //line sql.y:1095 + //line sql.y:1099 { yyVAL.tableName = TableName{Name: yyDollar[1].tableIdent} } - case 186: + case 187: yyDollar = yyS[yypt-3 : yypt+1] - //line sql.y:1099 + //line sql.y:1103 { yyVAL.tableName = TableName{Qualifier: yyDollar[1].tableIdent, Name: yyDollar[3].tableIdent} } - case 187: + case 188: yyDollar = yyS[yypt-0 : yypt+1] - //line sql.y:1104 + //line sql.y:1108 { yyVAL.expr = nil } - case 188: + case 189: yyDollar = yyS[yypt-2 : yypt+1] - //line sql.y:1108 + //line sql.y:1112 { yyVAL.expr = yyDollar[2].expr } - case 189: + case 190: yyDollar = yyS[yypt-1 : yypt+1] - //line sql.y:1114 + //line sql.y:1118 { yyVAL.expr = yyDollar[1].expr } - case 190: + case 191: yyDollar = yyS[yypt-3 : yypt+1] - //line sql.y:1118 + //line sql.y:1122 { yyVAL.expr = &AndExpr{Left: yyDollar[1].expr, Right: yyDollar[3].expr} } - case 191: + case 192: yyDollar = yyS[yypt-3 : yypt+1] - //line sql.y:1122 + //line sql.y:1126 { yyVAL.expr = &OrExpr{Left: yyDollar[1].expr, Right: yyDollar[3].expr} } - case 192: + case 193: yyDollar = yyS[yypt-2 : yypt+1] - //line sql.y:1126 + //line sql.y:1130 { yyVAL.expr = &NotExpr{Expr: yyDollar[2].expr} } - case 193: + case 194: yyDollar = yyS[yypt-3 : yypt+1] - //line sql.y:1130 + //line sql.y:1134 { yyVAL.expr = &IsExpr{Operator: yyDollar[3].str, Expr: yyDollar[1].expr} } - case 194: + case 195: yyDollar = yyS[yypt-1 : yypt+1] - //line sql.y:1134 + //line sql.y:1138 { yyVAL.expr = yyDollar[1].expr } - case 195: + case 196: yyDollar = yyS[yypt-2 : yypt+1] - //line sql.y:1138 + //line sql.y:1142 { yyVAL.expr = &Default{ColName: yyDollar[2].str} } - case 196: + case 197: yyDollar = yyS[yypt-0 : yypt+1] - //line sql.y:1144 + //line sql.y:1148 { yyVAL.str = "" } - case 197: + case 198: yyDollar = yyS[yypt-3 : yypt+1] - //line sql.y:1148 + //line sql.y:1152 { yyVAL.str = string(yyDollar[2].bytes) } - case 198: + case 199: yyDollar = yyS[yypt-1 : yypt+1] - //line sql.y:1154 + //line sql.y:1158 { yyVAL.boolVal = BoolVal(true) } - case 199: + case 200: yyDollar = yyS[yypt-1 : yypt+1] - //line sql.y:1158 + //line sql.y:1162 { yyVAL.boolVal = BoolVal(false) } - case 200: + case 201: yyDollar = yyS[yypt-3 : yypt+1] - //line sql.y:1164 + //line sql.y:1168 { yyVAL.expr = &ComparisonExpr{Left: yyDollar[1].expr, Operator: yyDollar[2].str, Right: yyDollar[3].expr} } - case 201: + case 202: yyDollar = yyS[yypt-3 : yypt+1] - //line sql.y:1168 + //line sql.y:1172 { yyVAL.expr = &ComparisonExpr{Left: yyDollar[1].expr, Operator: InStr, Right: yyDollar[3].colTuple} } - case 202: + case 203: yyDollar = yyS[yypt-4 : yypt+1] - //line sql.y:1172 + //line sql.y:1176 { yyVAL.expr = &ComparisonExpr{Left: yyDollar[1].expr, Operator: NotInStr, Right: yyDollar[4].colTuple} } - case 203: + case 204: yyDollar = yyS[yypt-4 : yypt+1] - //line sql.y:1176 + //line sql.y:1180 { yyVAL.expr = &ComparisonExpr{Left: yyDollar[1].expr, Operator: LikeStr, Right: yyDollar[3].expr, Escape: yyDollar[4].expr} } - case 204: + case 205: yyDollar = yyS[yypt-5 : yypt+1] - //line sql.y:1180 + //line sql.y:1184 { yyVAL.expr = &ComparisonExpr{Left: yyDollar[1].expr, Operator: NotLikeStr, Right: yyDollar[4].expr, Escape: yyDollar[5].expr} } - case 205: + case 206: yyDollar = yyS[yypt-3 : yypt+1] - //line sql.y:1184 + //line sql.y:1188 { yyVAL.expr = &ComparisonExpr{Left: yyDollar[1].expr, Operator: RegexpStr, Right: yyDollar[3].expr} } - case 206: + case 207: yyDollar = yyS[yypt-4 : yypt+1] - //line sql.y:1188 + //line sql.y:1192 { yyVAL.expr = &ComparisonExpr{Left: yyDollar[1].expr, Operator: NotRegexpStr, Right: yyDollar[4].expr} } - case 207: + case 208: yyDollar = yyS[yypt-5 : yypt+1] - //line sql.y:1192 + //line sql.y:1196 { yyVAL.expr = &RangeCond{Left: yyDollar[1].expr, Operator: BetweenStr, From: yyDollar[3].expr, To: yyDollar[5].expr} } - case 208: + case 209: yyDollar = yyS[yypt-6 : yypt+1] - //line sql.y:1196 + //line sql.y:1200 { yyVAL.expr = &RangeCond{Left: yyDollar[1].expr, Operator: NotBetweenStr, From: yyDollar[4].expr, To: yyDollar[6].expr} } - case 209: + case 210: yyDollar = yyS[yypt-2 : yypt+1] - //line sql.y:1200 + //line sql.y:1204 { yyVAL.expr = &ExistsExpr{Subquery: yyDollar[2].subquery} } - case 210: + case 211: yyDollar = yyS[yypt-1 : yypt+1] - //line sql.y:1206 + //line sql.y:1210 { yyVAL.str = IsNullStr } - case 211: + case 212: yyDollar = yyS[yypt-2 : yypt+1] - //line sql.y:1210 + //line sql.y:1214 { yyVAL.str = IsNotNullStr } - case 212: + case 213: yyDollar = yyS[yypt-1 : yypt+1] - //line sql.y:1214 + //line sql.y:1218 { yyVAL.str = IsTrueStr } - case 213: + case 214: yyDollar = yyS[yypt-2 : yypt+1] - //line sql.y:1218 + //line sql.y:1222 { yyVAL.str = IsNotTrueStr } - case 214: + case 215: yyDollar = yyS[yypt-1 : yypt+1] - //line sql.y:1222 + //line sql.y:1226 { yyVAL.str = IsFalseStr } - case 215: + case 216: yyDollar = yyS[yypt-2 : yypt+1] - //line sql.y:1226 + //line sql.y:1230 { yyVAL.str = IsNotFalseStr } - case 216: - yyDollar = yyS[yypt-1 : yypt+1] - //line sql.y:1232 - { - yyVAL.str = EqualStr - } case 217: yyDollar = yyS[yypt-1 : yypt+1] //line sql.y:1236 { - yyVAL.str = LessThanStr + yyVAL.str = EqualStr } case 218: yyDollar = yyS[yypt-1 : yypt+1] //line sql.y:1240 { - yyVAL.str = GreaterThanStr + yyVAL.str = LessThanStr } case 219: yyDollar = yyS[yypt-1 : yypt+1] //line sql.y:1244 { - yyVAL.str = LessEqualStr + yyVAL.str = GreaterThanStr } case 220: yyDollar = yyS[yypt-1 : yypt+1] //line sql.y:1248 { - yyVAL.str = GreaterEqualStr + yyVAL.str = LessEqualStr } case 221: yyDollar = yyS[yypt-1 : yypt+1] //line sql.y:1252 { - yyVAL.str = NotEqualStr + yyVAL.str = GreaterEqualStr } case 222: yyDollar = yyS[yypt-1 : yypt+1] //line sql.y:1256 { - yyVAL.str = NullSafeNotEqualStr + yyVAL.str = NotEqualStr } case 223: - yyDollar = yyS[yypt-0 : yypt+1] - //line sql.y:1261 + yyDollar = yyS[yypt-1 : yypt+1] + //line sql.y:1260 { - yyVAL.expr = nil + yyVAL.str = NullSafeNotEqualStr } case 224: - yyDollar = yyS[yypt-2 : yypt+1] + yyDollar = yyS[yypt-0 : yypt+1] //line sql.y:1265 { - yyVAL.expr = yyDollar[2].expr + yyVAL.expr = nil } case 225: - yyDollar = yyS[yypt-1 : yypt+1] - //line sql.y:1271 + yyDollar = yyS[yypt-2 : yypt+1] + //line sql.y:1269 { - yyVAL.colTuple = yyDollar[1].valTuple + yyVAL.expr = yyDollar[2].expr } case 226: yyDollar = yyS[yypt-1 : yypt+1] //line sql.y:1275 { - yyVAL.colTuple = yyDollar[1].subquery + yyVAL.colTuple = yyDollar[1].valTuple } case 227: yyDollar = yyS[yypt-1 : yypt+1] //line sql.y:1279 { - yyVAL.colTuple = ListArg(yyDollar[1].bytes) + yyVAL.colTuple = yyDollar[1].subquery } case 228: - yyDollar = yyS[yypt-3 : yypt+1] - //line sql.y:1285 + yyDollar = yyS[yypt-1 : yypt+1] + //line sql.y:1283 { - yyVAL.subquery = &Subquery{yyDollar[2].selStmt} + yyVAL.colTuple = ListArg(yyDollar[1].bytes) } case 229: - yyDollar = yyS[yypt-1 : yypt+1] - //line sql.y:1291 + yyDollar = yyS[yypt-3 : yypt+1] + //line sql.y:1289 { - yyVAL.exprs = Exprs{yyDollar[1].expr} + yyVAL.subquery = &Subquery{yyDollar[2].selStmt} } case 230: - yyDollar = yyS[yypt-3 : yypt+1] + yyDollar = yyS[yypt-1 : yypt+1] //line sql.y:1295 { - yyVAL.exprs = append(yyDollar[1].exprs, yyDollar[3].expr) + yyVAL.exprs = Exprs{yyDollar[1].expr} } case 231: - yyDollar = yyS[yypt-1 : yypt+1] - //line sql.y:1301 + yyDollar = yyS[yypt-3 : yypt+1] + //line sql.y:1299 { - yyVAL.expr = yyDollar[1].expr + yyVAL.exprs = append(yyDollar[1].exprs, yyDollar[3].expr) } case 232: yyDollar = yyS[yypt-1 : yypt+1] //line sql.y:1305 { - yyVAL.expr = yyDollar[1].boolVal + yyVAL.expr = yyDollar[1].expr } case 233: yyDollar = yyS[yypt-1 : yypt+1] //line sql.y:1309 { - yyVAL.expr = yyDollar[1].colName + yyVAL.expr = yyDollar[1].boolVal } case 234: yyDollar = yyS[yypt-1 : yypt+1] //line sql.y:1313 { - yyVAL.expr = yyDollar[1].expr + yyVAL.expr = yyDollar[1].colName } case 235: yyDollar = yyS[yypt-1 : yypt+1] //line sql.y:1317 { - yyVAL.expr = yyDollar[1].subquery + yyVAL.expr = yyDollar[1].expr } case 236: - yyDollar = yyS[yypt-3 : yypt+1] + yyDollar = yyS[yypt-1 : yypt+1] //line sql.y:1321 { - yyVAL.expr = &BinaryExpr{Left: yyDollar[1].expr, Operator: BitAndStr, Right: yyDollar[3].expr} + yyVAL.expr = yyDollar[1].subquery } case 237: yyDollar = yyS[yypt-3 : yypt+1] //line sql.y:1325 { - yyVAL.expr = &BinaryExpr{Left: yyDollar[1].expr, Operator: BitOrStr, Right: yyDollar[3].expr} + yyVAL.expr = &BinaryExpr{Left: yyDollar[1].expr, Operator: BitAndStr, Right: yyDollar[3].expr} } case 238: yyDollar = yyS[yypt-3 : yypt+1] //line sql.y:1329 { - yyVAL.expr = &BinaryExpr{Left: yyDollar[1].expr, Operator: BitXorStr, Right: yyDollar[3].expr} + yyVAL.expr = &BinaryExpr{Left: yyDollar[1].expr, Operator: BitOrStr, Right: yyDollar[3].expr} } case 239: yyDollar = yyS[yypt-3 : yypt+1] //line sql.y:1333 { - yyVAL.expr = &BinaryExpr{Left: yyDollar[1].expr, Operator: PlusStr, Right: yyDollar[3].expr} + yyVAL.expr = &BinaryExpr{Left: yyDollar[1].expr, Operator: BitXorStr, Right: yyDollar[3].expr} } case 240: yyDollar = yyS[yypt-3 : yypt+1] //line sql.y:1337 { - yyVAL.expr = &BinaryExpr{Left: yyDollar[1].expr, Operator: MinusStr, Right: yyDollar[3].expr} + yyVAL.expr = &BinaryExpr{Left: yyDollar[1].expr, Operator: PlusStr, Right: yyDollar[3].expr} } case 241: yyDollar = yyS[yypt-3 : yypt+1] //line sql.y:1341 { - yyVAL.expr = &BinaryExpr{Left: yyDollar[1].expr, Operator: MultStr, Right: yyDollar[3].expr} + yyVAL.expr = &BinaryExpr{Left: yyDollar[1].expr, Operator: MinusStr, Right: yyDollar[3].expr} } case 242: yyDollar = yyS[yypt-3 : yypt+1] //line sql.y:1345 { - yyVAL.expr = &BinaryExpr{Left: yyDollar[1].expr, Operator: DivStr, Right: yyDollar[3].expr} + yyVAL.expr = &BinaryExpr{Left: yyDollar[1].expr, Operator: MultStr, Right: yyDollar[3].expr} } case 243: yyDollar = yyS[yypt-3 : yypt+1] //line sql.y:1349 { - yyVAL.expr = &BinaryExpr{Left: yyDollar[1].expr, Operator: IntDivStr, Right: yyDollar[3].expr} + yyVAL.expr = &BinaryExpr{Left: yyDollar[1].expr, Operator: DivStr, Right: yyDollar[3].expr} } case 244: yyDollar = yyS[yypt-3 : yypt+1] //line sql.y:1353 { - yyVAL.expr = &BinaryExpr{Left: yyDollar[1].expr, Operator: ModStr, Right: yyDollar[3].expr} + yyVAL.expr = &BinaryExpr{Left: yyDollar[1].expr, Operator: IntDivStr, Right: yyDollar[3].expr} } case 245: yyDollar = yyS[yypt-3 : yypt+1] @@ -3013,17 +3026,23 @@ yydefault: yyDollar = yyS[yypt-3 : yypt+1] //line sql.y:1361 { - yyVAL.expr = &BinaryExpr{Left: yyDollar[1].expr, Operator: ShiftLeftStr, Right: yyDollar[3].expr} + yyVAL.expr = &BinaryExpr{Left: yyDollar[1].expr, Operator: ModStr, Right: yyDollar[3].expr} } case 247: yyDollar = yyS[yypt-3 : yypt+1] //line sql.y:1365 { - yyVAL.expr = &BinaryExpr{Left: yyDollar[1].expr, Operator: ShiftRightStr, Right: yyDollar[3].expr} + yyVAL.expr = &BinaryExpr{Left: yyDollar[1].expr, Operator: ShiftLeftStr, Right: yyDollar[3].expr} } case 248: - yyDollar = yyS[yypt-2 : yypt+1] + yyDollar = yyS[yypt-3 : yypt+1] //line sql.y:1369 + { + yyVAL.expr = &BinaryExpr{Left: yyDollar[1].expr, Operator: ShiftRightStr, Right: yyDollar[3].expr} + } + case 249: + yyDollar = yyS[yypt-2 : yypt+1] + //line sql.y:1373 { if num, ok := yyDollar[2].expr.(*SQLVal); ok && num.Type == IntVal { yyVAL.expr = num @@ -3031,9 +3050,9 @@ yydefault: yyVAL.expr = &UnaryExpr{Operator: UPlusStr, Expr: yyDollar[2].expr} } } - case 249: + case 250: yyDollar = yyS[yypt-2 : yypt+1] - //line sql.y:1377 + //line sql.y:1381 { if num, ok := yyDollar[2].expr.(*SQLVal); ok && num.Type == IntVal { // Handle double negative @@ -3047,21 +3066,21 @@ yydefault: yyVAL.expr = &UnaryExpr{Operator: UMinusStr, Expr: yyDollar[2].expr} } } - case 250: + case 251: yyDollar = yyS[yypt-2 : yypt+1] - //line sql.y:1391 + //line sql.y:1395 { yyVAL.expr = &UnaryExpr{Operator: TildaStr, Expr: yyDollar[2].expr} } - case 251: + case 252: yyDollar = yyS[yypt-2 : yypt+1] - //line sql.y:1395 + //line sql.y:1399 { yyVAL.expr = &UnaryExpr{Operator: BangStr, Expr: yyDollar[2].expr} } - case 252: + case 253: yyDollar = yyS[yypt-3 : yypt+1] - //line sql.y:1399 + //line sql.y:1403 { // This rule prevents the usage of INTERVAL // as a function. If support is needed for that, @@ -3069,136 +3088,130 @@ yydefault: // will be non-trivial because of grammar conflicts. yyVAL.expr = &IntervalExpr{Expr: yyDollar[2].expr, Unit: yyDollar[3].colIdent.String()} } - case 257: + case 258: yyDollar = yyS[yypt-4 : yypt+1] - //line sql.y:1417 + //line sql.y:1421 { yyVAL.expr = &FuncExpr{Name: yyDollar[1].colIdent, Exprs: yyDollar[3].selectExprs} } - case 258: + case 259: yyDollar = yyS[yypt-5 : yypt+1] - //line sql.y:1421 + //line sql.y:1425 { yyVAL.expr = &FuncExpr{Name: yyDollar[1].colIdent, Distinct: true, Exprs: yyDollar[4].selectExprs} } - case 259: + case 260: yyDollar = yyS[yypt-6 : yypt+1] - //line sql.y:1425 + //line sql.y:1429 { yyVAL.expr = &FuncExpr{Qualifier: yyDollar[1].tableIdent, Name: yyDollar[3].colIdent, Exprs: yyDollar[5].selectExprs} } - case 260: + case 261: yyDollar = yyS[yypt-6 : yypt+1] - //line sql.y:1435 + //line sql.y:1439 { yyVAL.expr = &ConvertExpr{Expr: yyDollar[3].expr, Type: yyDollar[5].convertType} } - case 261: + case 262: yyDollar = yyS[yypt-6 : yypt+1] - //line sql.y:1439 + //line sql.y:1443 { yyVAL.expr = &SubstrExpr{Name: yyDollar[3].colName, From: yyDollar[5].expr, To: nil} } - case 262: + case 263: yyDollar = yyS[yypt-8 : yypt+1] - //line sql.y:1443 + //line sql.y:1447 { yyVAL.expr = &SubstrExpr{Name: yyDollar[3].colName, From: yyDollar[5].expr, To: yyDollar[7].expr} } - case 263: + case 264: yyDollar = yyS[yypt-7 : yypt+1] - //line sql.y:1447 + //line sql.y:1451 { yyVAL.expr = &GroupConcatExpr{Distinct: yyDollar[3].str, Exprs: yyDollar[4].selectExprs, OrderBy: yyDollar[5].orderBy, Separator: yyDollar[6].str} } - case 264: + case 265: yyDollar = yyS[yypt-5 : yypt+1] - //line sql.y:1451 + //line sql.y:1455 { yyVAL.expr = &CaseExpr{Expr: yyDollar[2].expr, Whens: yyDollar[3].whens, Else: yyDollar[4].expr} } - case 265: + case 266: yyDollar = yyS[yypt-4 : yypt+1] - //line sql.y:1455 + //line sql.y:1459 { yyVAL.expr = &ValuesFuncExpr{Name: yyDollar[3].colName} } - case 266: + case 267: yyDollar = yyS[yypt-1 : yypt+1] - //line sql.y:1465 + //line sql.y:1469 { yyVAL.expr = &TimeExpr{Expr: NewColIdent("current_timestamp")} } - case 267: + case 268: yyDollar = yyS[yypt-1 : yypt+1] - //line sql.y:1470 + //line sql.y:1474 { yyVAL.expr = &TimeExpr{Expr: NewColIdent("current_date")} } - case 268: + case 269: yyDollar = yyS[yypt-1 : yypt+1] - //line sql.y:1475 + //line sql.y:1479 { yyVAL.expr = &TimeExpr{Expr: NewColIdent("current_time")} } - case 269: - yyDollar = yyS[yypt-4 : yypt+1] - //line sql.y:1485 - { - yyVAL.expr = &FuncExpr{Name: NewColIdent("if"), Exprs: yyDollar[3].selectExprs} - } case 270: yyDollar = yyS[yypt-4 : yypt+1] //line sql.y:1489 { - yyVAL.expr = &FuncExpr{Name: NewColIdent("mod"), Exprs: yyDollar[3].selectExprs} + yyVAL.expr = &FuncExpr{Name: NewColIdent("if"), Exprs: yyDollar[3].selectExprs} } case 271: yyDollar = yyS[yypt-4 : yypt+1] //line sql.y:1493 { - yyVAL.expr = &FuncExpr{Name: NewColIdent("replace"), Exprs: yyDollar[3].selectExprs} + yyVAL.expr = &FuncExpr{Name: NewColIdent("mod"), Exprs: yyDollar[3].selectExprs} } case 272: - yyDollar = yyS[yypt-2 : yypt+1] - //line sql.y:1499 + yyDollar = yyS[yypt-4 : yypt+1] + //line sql.y:1497 { - yyVAL.convertType = &ConvertType{Type: string(yyDollar[1].bytes), Length: yyDollar[2].optVal} + yyVAL.expr = &FuncExpr{Name: NewColIdent("replace"), Exprs: yyDollar[3].selectExprs} } case 273: - yyDollar = yyS[yypt-1 : yypt+1] + yyDollar = yyS[yypt-2 : yypt+1] //line sql.y:1503 { - yyVAL.convertType = &ConvertType{Type: string(yyDollar[1].bytes)} + yyVAL.convertType = &ConvertType{Type: string(yyDollar[1].bytes), Length: yyDollar[2].optVal} } case 274: - yyDollar = yyS[yypt-2 : yypt+1] + yyDollar = yyS[yypt-1 : yypt+1] //line sql.y:1507 { - yyVAL.convertType = &ConvertType{Type: string(yyDollar[1].bytes), Length: yyDollar[2].optVal} + yyVAL.convertType = &ConvertType{Type: string(yyDollar[1].bytes)} } case 275: yyDollar = yyS[yypt-2 : yypt+1] //line sql.y:1511 { - yyVAL.convertType = &ConvertType{Type: string(yyDollar[1].bytes)} - yyVAL.convertType.Length = yyDollar[2].LengthScaleOption.Length - yyVAL.convertType.Scale = yyDollar[2].LengthScaleOption.Scale + yyVAL.convertType = &ConvertType{Type: string(yyDollar[1].bytes), Length: yyDollar[2].optVal} } case 276: yyDollar = yyS[yypt-2 : yypt+1] - //line sql.y:1517 + //line sql.y:1515 { - yyVAL.convertType = &ConvertType{Type: string(yyDollar[1].bytes), Length: yyDollar[2].optVal} + yyVAL.convertType = &ConvertType{Type: string(yyDollar[1].bytes)} + yyVAL.convertType.Length = yyDollar[2].LengthScaleOption.Length + yyVAL.convertType.Scale = yyDollar[2].LengthScaleOption.Scale } case 277: - yyDollar = yyS[yypt-1 : yypt+1] + yyDollar = yyS[yypt-2 : yypt+1] //line sql.y:1521 { - yyVAL.convertType = &ConvertType{Type: string(yyDollar[1].bytes)} + yyVAL.convertType = &ConvertType{Type: string(yyDollar[1].bytes), Length: yyDollar[2].optVal} } case 278: - yyDollar = yyS[yypt-2 : yypt+1] + yyDollar = yyS[yypt-1 : yypt+1] //line sql.y:1525 { yyVAL.convertType = &ConvertType{Type: string(yyDollar[1].bytes)} @@ -3207,16 +3220,16 @@ yydefault: yyDollar = yyS[yypt-2 : yypt+1] //line sql.y:1529 { - yyVAL.convertType = &ConvertType{Type: string(yyDollar[1].bytes), Length: yyDollar[2].optVal} + yyVAL.convertType = &ConvertType{Type: string(yyDollar[1].bytes)} } case 280: - yyDollar = yyS[yypt-1 : yypt+1] + yyDollar = yyS[yypt-2 : yypt+1] //line sql.y:1533 { - yyVAL.convertType = &ConvertType{Type: string(yyDollar[1].bytes)} + yyVAL.convertType = &ConvertType{Type: string(yyDollar[1].bytes), Length: yyDollar[2].optVal} } case 281: - yyDollar = yyS[yypt-2 : yypt+1] + yyDollar = yyS[yypt-1 : yypt+1] //line sql.y:1537 { yyVAL.convertType = &ConvertType{Type: string(yyDollar[1].bytes)} @@ -3225,7 +3238,7 @@ yydefault: yyDollar = yyS[yypt-2 : yypt+1] //line sql.y:1541 { - yyVAL.convertType = &ConvertType{Type: string(yyDollar[1].bytes), Length: yyDollar[2].optVal} + yyVAL.convertType = &ConvertType{Type: string(yyDollar[1].bytes)} } case 283: yyDollar = yyS[yypt-2 : yypt+1] @@ -3258,316 +3271,322 @@ yydefault: yyVAL.convertType = &ConvertType{Type: string(yyDollar[1].bytes), Length: yyDollar[2].optVal} } case 288: - yyDollar = yyS[yypt-1 : yypt+1] + yyDollar = yyS[yypt-2 : yypt+1] //line sql.y:1565 { - yyVAL.convertType = &ConvertType{Type: string(yyDollar[1].bytes)} + yyVAL.convertType = &ConvertType{Type: string(yyDollar[1].bytes), Length: yyDollar[2].optVal} } case 289: + yyDollar = yyS[yypt-1 : yypt+1] + //line sql.y:1569 + { + yyVAL.convertType = &ConvertType{Type: string(yyDollar[1].bytes)} + } + case 290: yyDollar = yyS[yypt-0 : yypt+1] - //line sql.y:1570 + //line sql.y:1574 { yyVAL.expr = nil } - case 290: + case 291: yyDollar = yyS[yypt-1 : yypt+1] - //line sql.y:1574 + //line sql.y:1578 { yyVAL.expr = yyDollar[1].expr } - case 291: + case 292: yyDollar = yyS[yypt-0 : yypt+1] - //line sql.y:1579 + //line sql.y:1583 { yyVAL.str = string("") } - case 292: + case 293: yyDollar = yyS[yypt-2 : yypt+1] - //line sql.y:1583 + //line sql.y:1587 { yyVAL.str = " separator '" + string(yyDollar[2].bytes) + "'" } - case 293: + case 294: yyDollar = yyS[yypt-1 : yypt+1] - //line sql.y:1589 + //line sql.y:1593 { yyVAL.whens = []*When{yyDollar[1].when} } - case 294: + case 295: yyDollar = yyS[yypt-2 : yypt+1] - //line sql.y:1593 + //line sql.y:1597 { yyVAL.whens = append(yyDollar[1].whens, yyDollar[2].when) } - case 295: + case 296: yyDollar = yyS[yypt-4 : yypt+1] - //line sql.y:1599 + //line sql.y:1603 { yyVAL.when = &When{Cond: yyDollar[2].expr, Val: yyDollar[4].expr} } - case 296: + case 297: yyDollar = yyS[yypt-0 : yypt+1] - //line sql.y:1604 + //line sql.y:1608 { yyVAL.expr = nil } - case 297: + case 298: yyDollar = yyS[yypt-2 : yypt+1] - //line sql.y:1608 + //line sql.y:1612 { yyVAL.expr = yyDollar[2].expr } - case 298: + case 299: yyDollar = yyS[yypt-1 : yypt+1] - //line sql.y:1614 + //line sql.y:1618 { yyVAL.colName = &ColName{Name: yyDollar[1].colIdent} } - case 299: + case 300: yyDollar = yyS[yypt-3 : yypt+1] - //line sql.y:1618 + //line sql.y:1622 { yyVAL.colName = &ColName{Qualifier: TableName{Name: yyDollar[1].tableIdent}, Name: yyDollar[3].colIdent} } - case 300: + case 301: yyDollar = yyS[yypt-5 : yypt+1] - //line sql.y:1622 + //line sql.y:1626 { yyVAL.colName = &ColName{Qualifier: TableName{Qualifier: yyDollar[1].tableIdent, Name: yyDollar[3].tableIdent}, Name: yyDollar[5].colIdent} } - case 301: - yyDollar = yyS[yypt-1 : yypt+1] - //line sql.y:1628 - { - yyVAL.expr = NewStrVal(yyDollar[1].bytes) - } case 302: yyDollar = yyS[yypt-1 : yypt+1] //line sql.y:1632 { - yyVAL.expr = NewHexVal(yyDollar[1].bytes) + yyVAL.expr = NewStrVal(yyDollar[1].bytes) } case 303: yyDollar = yyS[yypt-1 : yypt+1] //line sql.y:1636 { - yyVAL.expr = NewIntVal(yyDollar[1].bytes) + yyVAL.expr = NewHexVal(yyDollar[1].bytes) } case 304: yyDollar = yyS[yypt-1 : yypt+1] //line sql.y:1640 { - yyVAL.expr = NewFloatVal(yyDollar[1].bytes) + yyVAL.expr = NewIntVal(yyDollar[1].bytes) } case 305: yyDollar = yyS[yypt-1 : yypt+1] //line sql.y:1644 { - yyVAL.expr = NewHexNum(yyDollar[1].bytes) + yyVAL.expr = NewFloatVal(yyDollar[1].bytes) } case 306: yyDollar = yyS[yypt-1 : yypt+1] //line sql.y:1648 { - yyVAL.expr = NewValArg(yyDollar[1].bytes) + yyVAL.expr = NewHexNum(yyDollar[1].bytes) } case 307: yyDollar = yyS[yypt-1 : yypt+1] //line sql.y:1652 { - yyVAL.expr = &NullVal{} + yyVAL.expr = NewValArg(yyDollar[1].bytes) } case 308: + yyDollar = yyS[yypt-1 : yypt+1] + //line sql.y:1656 + { + yyVAL.expr = &NullVal{} + } + case 309: yyDollar = yyS[yypt-0 : yypt+1] - //line sql.y:1657 + //line sql.y:1661 { yyVAL.exprs = nil } - case 309: + case 310: yyDollar = yyS[yypt-3 : yypt+1] - //line sql.y:1661 + //line sql.y:1665 { yyVAL.exprs = yyDollar[3].exprs } - case 310: + case 311: yyDollar = yyS[yypt-0 : yypt+1] - //line sql.y:1666 + //line sql.y:1670 { yyVAL.expr = nil } - case 311: + case 312: yyDollar = yyS[yypt-2 : yypt+1] - //line sql.y:1670 + //line sql.y:1674 { yyVAL.expr = yyDollar[2].expr } - case 312: + case 313: yyDollar = yyS[yypt-0 : yypt+1] - //line sql.y:1675 + //line sql.y:1679 { yyVAL.orderBy = nil } - case 313: + case 314: yyDollar = yyS[yypt-3 : yypt+1] - //line sql.y:1679 + //line sql.y:1683 { yyVAL.orderBy = yyDollar[3].orderBy } - case 314: + case 315: yyDollar = yyS[yypt-1 : yypt+1] - //line sql.y:1685 + //line sql.y:1689 { yyVAL.orderBy = OrderBy{yyDollar[1].order} } - case 315: + case 316: yyDollar = yyS[yypt-3 : yypt+1] - //line sql.y:1689 + //line sql.y:1693 { yyVAL.orderBy = append(yyDollar[1].orderBy, yyDollar[3].order) } - case 316: + case 317: yyDollar = yyS[yypt-2 : yypt+1] - //line sql.y:1695 + //line sql.y:1699 { yyVAL.order = &Order{Expr: yyDollar[1].expr, Direction: yyDollar[2].str} } - case 317: + case 318: yyDollar = yyS[yypt-0 : yypt+1] - //line sql.y:1700 + //line sql.y:1704 { yyVAL.str = AscScr } - case 318: + case 319: yyDollar = yyS[yypt-1 : yypt+1] - //line sql.y:1704 + //line sql.y:1708 { yyVAL.str = AscScr } - case 319: + case 320: yyDollar = yyS[yypt-1 : yypt+1] - //line sql.y:1708 + //line sql.y:1712 { yyVAL.str = DescScr } - case 320: + case 321: yyDollar = yyS[yypt-0 : yypt+1] - //line sql.y:1713 + //line sql.y:1717 { yyVAL.limit = nil } - case 321: + case 322: yyDollar = yyS[yypt-2 : yypt+1] - //line sql.y:1717 + //line sql.y:1721 { yyVAL.limit = &Limit{Rowcount: yyDollar[2].expr} } - case 322: + case 323: yyDollar = yyS[yypt-4 : yypt+1] - //line sql.y:1721 + //line sql.y:1725 { yyVAL.limit = &Limit{Offset: yyDollar[2].expr, Rowcount: yyDollar[4].expr} } - case 323: + case 324: yyDollar = yyS[yypt-4 : yypt+1] - //line sql.y:1725 + //line sql.y:1729 { yyVAL.limit = &Limit{Offset: yyDollar[4].expr, Rowcount: yyDollar[2].expr} } - case 324: + case 325: yyDollar = yyS[yypt-2 : yypt+1] - //line sql.y:1738 + //line sql.y:1742 { yyVAL.ins = &Insert{Rows: yyDollar[2].values} } - case 325: + case 326: yyDollar = yyS[yypt-1 : yypt+1] - //line sql.y:1742 + //line sql.y:1746 { yyVAL.ins = &Insert{Rows: yyDollar[1].selStmt} } - case 326: + case 327: yyDollar = yyS[yypt-3 : yypt+1] - //line sql.y:1746 + //line sql.y:1750 { // Drop the redundant parenthesis. yyVAL.ins = &Insert{Rows: yyDollar[2].selStmt} } - case 327: + case 328: yyDollar = yyS[yypt-5 : yypt+1] - //line sql.y:1751 + //line sql.y:1755 { yyVAL.ins = &Insert{Columns: yyDollar[2].columns, Rows: yyDollar[5].values} } - case 328: + case 329: yyDollar = yyS[yypt-4 : yypt+1] - //line sql.y:1755 + //line sql.y:1759 { yyVAL.ins = &Insert{Columns: yyDollar[2].columns, Rows: yyDollar[4].selStmt} } - case 329: + case 330: yyDollar = yyS[yypt-6 : yypt+1] - //line sql.y:1759 + //line sql.y:1763 { // Drop the redundant parenthesis. yyVAL.ins = &Insert{Columns: yyDollar[2].columns, Rows: yyDollar[5].selStmt} } - case 330: + case 331: yyDollar = yyS[yypt-1 : yypt+1] - //line sql.y:1766 + //line sql.y:1770 { yyVAL.columns = Columns{yyDollar[1].colIdent} } - case 331: + case 332: yyDollar = yyS[yypt-3 : yypt+1] - //line sql.y:1770 + //line sql.y:1774 { yyVAL.columns = Columns{yyDollar[3].colIdent} } - case 332: + case 333: yyDollar = yyS[yypt-3 : yypt+1] - //line sql.y:1774 + //line sql.y:1778 { yyVAL.columns = append(yyVAL.columns, yyDollar[3].colIdent) } - case 333: + case 334: yyDollar = yyS[yypt-5 : yypt+1] - //line sql.y:1778 + //line sql.y:1782 { yyVAL.columns = append(yyVAL.columns, yyDollar[5].colIdent) } - case 334: + case 335: yyDollar = yyS[yypt-1 : yypt+1] - //line sql.y:1784 + //line sql.y:1788 { yyVAL.values = Values{yyDollar[1].valTuple} } - case 335: + case 336: yyDollar = yyS[yypt-3 : yypt+1] - //line sql.y:1788 + //line sql.y:1792 { yyVAL.values = append(yyDollar[1].values, yyDollar[3].valTuple) } - case 336: + case 337: yyDollar = yyS[yypt-1 : yypt+1] - //line sql.y:1794 + //line sql.y:1798 { yyVAL.valTuple = yyDollar[1].valTuple } - case 337: + case 338: yyDollar = yyS[yypt-2 : yypt+1] - //line sql.y:1798 + //line sql.y:1802 { yyVAL.valTuple = ValTuple{} } - case 338: + case 339: yyDollar = yyS[yypt-3 : yypt+1] - //line sql.y:1804 + //line sql.y:1808 { yyVAL.valTuple = ValTuple(yyDollar[2].exprs) } - case 339: + case 340: yyDollar = yyS[yypt-1 : yypt+1] - //line sql.y:1810 + //line sql.y:1814 { if len(yyDollar[1].valTuple) == 1 { yyVAL.expr = &ParenExpr{yyDollar[1].valTuple[0]} @@ -3575,75 +3594,69 @@ yydefault: yyVAL.expr = yyDollar[1].valTuple } } - case 340: + case 341: yyDollar = yyS[yypt-1 : yypt+1] - //line sql.y:1820 + //line sql.y:1824 { yyVAL.updateExprs = UpdateExprs{yyDollar[1].updateExpr} } - case 341: + case 342: yyDollar = yyS[yypt-3 : yypt+1] - //line sql.y:1824 + //line sql.y:1828 { yyVAL.updateExprs = append(yyDollar[1].updateExprs, yyDollar[3].updateExpr) } - case 342: + case 343: yyDollar = yyS[yypt-3 : yypt+1] - //line sql.y:1830 + //line sql.y:1834 { yyVAL.updateExpr = &UpdateExpr{Name: yyDollar[1].colName, Expr: yyDollar[3].expr} } - case 343: + case 344: yyDollar = yyS[yypt-0 : yypt+1] - //line sql.y:1835 + //line sql.y:1839 { yyVAL.byt = 0 } - case 344: + case 345: yyDollar = yyS[yypt-2 : yypt+1] - //line sql.y:1837 + //line sql.y:1841 { yyVAL.byt = 1 } - case 345: + case 346: yyDollar = yyS[yypt-0 : yypt+1] - //line sql.y:1840 + //line sql.y:1844 { yyVAL.empty = struct{}{} } - case 346: + case 347: yyDollar = yyS[yypt-3 : yypt+1] - //line sql.y:1842 + //line sql.y:1846 { yyVAL.empty = struct{}{} } - case 347: + case 348: yyDollar = yyS[yypt-0 : yypt+1] - //line sql.y:1845 + //line sql.y:1849 { yyVAL.str = "" } - case 348: + case 349: yyDollar = yyS[yypt-1 : yypt+1] - //line sql.y:1847 + //line sql.y:1851 { yyVAL.str = IgnoreStr } - case 349: - yyDollar = yyS[yypt-0 : yypt+1] - //line sql.y:1850 - { - yyVAL.empty = struct{}{} - } case 350: - yyDollar = yyS[yypt-1 : yypt+1] - //line sql.y:1852 + yyDollar = yyS[yypt-0 : yypt+1] + //line sql.y:1854 { yyVAL.empty = struct{}{} } case 351: yyDollar = yyS[yypt-1 : yypt+1] - //line sql.y:1854 + //line sql.y:1856 { yyVAL.empty = struct{}{} } @@ -3651,7 +3664,7 @@ yydefault: yyDollar = yyS[yypt-1 : yypt+1] //line sql.y:1858 { - yyVAL.colIdent = NewColIdent(string(yyDollar[1].bytes)) + yyVAL.empty = struct{}{} } case 353: yyDollar = yyS[yypt-1 : yypt+1] @@ -3659,17 +3672,17 @@ yydefault: { yyVAL.colIdent = NewColIdent(string(yyDollar[1].bytes)) } - case 355: + case 354: yyDollar = yyS[yypt-1 : yypt+1] - //line sql.y:1869 + //line sql.y:1866 { yyVAL.colIdent = NewColIdent(string(yyDollar[1].bytes)) } case 356: yyDollar = yyS[yypt-1 : yypt+1] - //line sql.y:1875 + //line sql.y:1873 { - yyVAL.tableIdent = NewTableIdent(string(yyDollar[1].bytes)) + yyVAL.colIdent = NewColIdent(string(yyDollar[1].bytes)) } case 357: yyDollar = yyS[yypt-1 : yypt+1] @@ -3677,42 +3690,54 @@ yydefault: { yyVAL.tableIdent = NewTableIdent(string(yyDollar[1].bytes)) } - case 359: + case 358: yyDollar = yyS[yypt-1 : yypt+1] - //line sql.y:1886 + //line sql.y:1883 { yyVAL.tableIdent = NewTableIdent(string(yyDollar[1].bytes)) } - case 467: + case 360: yyDollar = yyS[yypt-1 : yypt+1] - //line sql.y:2019 + //line sql.y:1890 + { + yyVAL.tableIdent = NewTableIdent(string(yyDollar[1].bytes)) + } + case 469: + yyDollar = yyS[yypt-1 : yypt+1] + //line sql.y:2024 { if incNesting(yylex) { yylex.Error("max nesting level reached") return 1 } } - case 468: + case 470: yyDollar = yyS[yypt-1 : yypt+1] - //line sql.y:2028 + //line sql.y:2033 { decNesting(yylex) } - case 469: + case 471: yyDollar = yyS[yypt-0 : yypt+1] - //line sql.y:2033 + //line sql.y:2038 { forceEOF(yylex) } - case 470: + case 472: + yyDollar = yyS[yypt-0 : yypt+1] + //line sql.y:2043 + { + forceEOF(yylex) + } + case 473: yyDollar = yyS[yypt-1 : yypt+1] - //line sql.y:2037 + //line sql.y:2047 { forceEOF(yylex) } - case 471: + case 474: yyDollar = yyS[yypt-1 : yypt+1] - //line sql.y:2041 + //line sql.y:2051 { forceEOF(yylex) } diff --git a/vendor/github.com/CovenantSQL/sqlparser/sql.y b/vendor/github.com/CovenantSQL/sqlparser/sql.y index 6e70d8a16..52c795e3c 100644 --- a/vendor/github.com/CovenantSQL/sqlparser/sql.y +++ b/vendor/github.com/CovenantSQL/sqlparser/sql.y @@ -136,7 +136,7 @@ func forceEOF(yylex interface{}) { // DDL Tokens %token CREATE ALTER DROP RENAME ADD %token TABLE INDEX TO IGNORE IF UNIQUE PRIMARY COLUMN CONSTRAINT FOREIGN -%token SHOW DESCRIBE DATE ESCAPE +%token SHOW DESCRIBE DATE ESCAPE EXPLAIN // Type Tokens %token TINYINT SMALLINT MEDIUMINT INT INTEGER BIGINT INTNUM @@ -845,6 +845,10 @@ other_statement: { $$ = &Show{Type: "table", OnTable: $2} } +| EXPLAIN force_eof + { + $$ = &Explain{} + } comment_opt: { @@ -1968,6 +1972,7 @@ reserved_keyword: | VALUES | WHEN | WHERE +| EXPLAIN /* These are non-reserved Vitess, because they don't cause conflicts in the grammar. @@ -2029,6 +2034,11 @@ closeb: decNesting(yylex) } +force_eof: + { + forceEOF(yylex) + } + ddl_force_eof: { forceEOF(yylex) diff --git a/vendor/github.com/CovenantSQL/sqlparser/token.go b/vendor/github.com/CovenantSQL/sqlparser/token.go index 74ed904e6..f82d747c9 100644 --- a/vendor/github.com/CovenantSQL/sqlparser/token.go +++ b/vendor/github.com/CovenantSQL/sqlparser/token.go @@ -304,6 +304,7 @@ var keywords = map[string]int{ "year": YEAR, "year_month": UNUSED, "zerofill": ZEROFILL, + "explain": EXPLAIN, } // keywordStrings contains the reverse mapping of token to keyword strings diff --git a/vendor/github.com/Knetic/govaluate/CONTRIBUTORS b/vendor/github.com/Knetic/govaluate/CONTRIBUTORS new file mode 100644 index 000000000..364e5cf94 --- /dev/null +++ b/vendor/github.com/Knetic/govaluate/CONTRIBUTORS @@ -0,0 +1,12 @@ +This library was authored by George Lester, and contains contributions from: + +vjeantet (regex support) +iasci (ternary operator) +oxtoacart (parameter structures, deferred parameter retrieval) +wmiller848 (bitwise operators) +prashantv (optimization of bools) +dpaolella (exposure of variables used in an expression) +benpaxton (fix for missing type checks during literal elide process) +abrander (panic-finding testing tool) +xfennec (fix for dates being parsed in the current Location) +bgaifullin (lifting restriction on complex/struct types) \ No newline at end of file diff --git a/vendor/github.com/Knetic/govaluate/EvaluableExpression.go b/vendor/github.com/Knetic/govaluate/EvaluableExpression.go new file mode 100644 index 000000000..8f43112a6 --- /dev/null +++ b/vendor/github.com/Knetic/govaluate/EvaluableExpression.go @@ -0,0 +1,272 @@ +package govaluate + +import ( + "errors" + "fmt" +) + +const isoDateFormat string = "2006-01-02T15:04:05.999999999Z0700" +const shortCircuitHolder int = -1 + +var DUMMY_PARAMETERS = MapParameters(map[string]interface{}{}) + +/* + EvaluableExpression represents a set of ExpressionTokens which, taken together, + are an expression that can be evaluated down into a single value. +*/ +type EvaluableExpression struct { + + /* + Represents the query format used to output dates. Typically only used when creating SQL or Mongo queries from an expression. + Defaults to the complete ISO8601 format, including nanoseconds. + */ + QueryDateFormat string + + /* + Whether or not to safely check types when evaluating. + If true, this library will return error messages when invalid types are used. + If false, the library will panic when operators encounter types they can't use. + + This is exclusively for users who need to squeeze every ounce of speed out of the library as they can, + and you should only set this to false if you know exactly what you're doing. + */ + ChecksTypes bool + + tokens []ExpressionToken + evaluationStages *evaluationStage + inputExpression string +} + +/* + Parses a new EvaluableExpression from the given [expression] string. + Returns an error if the given expression has invalid syntax. +*/ +func NewEvaluableExpression(expression string) (*EvaluableExpression, error) { + + functions := make(map[string]ExpressionFunction) + return NewEvaluableExpressionWithFunctions(expression, functions) +} + +/* + Similar to [NewEvaluableExpression], except that instead of a string, an already-tokenized expression is given. + This is useful in cases where you may be generating an expression automatically, or using some other parser (e.g., to parse from a query language) +*/ +func NewEvaluableExpressionFromTokens(tokens []ExpressionToken) (*EvaluableExpression, error) { + + var ret *EvaluableExpression + var err error + + ret = new(EvaluableExpression) + ret.QueryDateFormat = isoDateFormat + + err = checkBalance(tokens) + if err != nil { + return nil, err + } + + err = checkExpressionSyntax(tokens) + if err != nil { + return nil, err + } + + ret.tokens, err = optimizeTokens(tokens) + if err != nil { + return nil, err + } + + ret.evaluationStages, err = planStages(ret.tokens) + if err != nil { + return nil, err + } + + ret.ChecksTypes = true + return ret, nil +} + +/* + Similar to [NewEvaluableExpression], except enables the use of user-defined functions. + Functions passed into this will be available to the expression. +*/ +func NewEvaluableExpressionWithFunctions(expression string, functions map[string]ExpressionFunction) (*EvaluableExpression, error) { + + var ret *EvaluableExpression + var err error + + ret = new(EvaluableExpression) + ret.QueryDateFormat = isoDateFormat + ret.inputExpression = expression + + ret.tokens, err = parseTokens(expression, functions) + if err != nil { + return nil, err + } + + err = checkBalance(ret.tokens) + if err != nil { + return nil, err + } + + err = checkExpressionSyntax(ret.tokens) + if err != nil { + return nil, err + } + + ret.tokens, err = optimizeTokens(ret.tokens) + if err != nil { + return nil, err + } + + ret.evaluationStages, err = planStages(ret.tokens) + if err != nil { + return nil, err + } + + ret.ChecksTypes = true + return ret, nil +} + +/* + Same as `Eval`, but automatically wraps a map of parameters into a `govalute.Parameters` structure. +*/ +func (this EvaluableExpression) Evaluate(parameters map[string]interface{}) (interface{}, error) { + + if parameters == nil { + return this.Eval(nil) + } + return this.Eval(MapParameters(parameters)) +} + +/* + Runs the entire expression using the given [parameters]. + e.g., If the expression contains a reference to the variable "foo", it will be taken from `parameters.Get("foo")`. + + This function returns errors if the combination of expression and parameters cannot be run, + such as if a variable in the expression is not present in [parameters]. + + In all non-error circumstances, this returns the single value result of the expression and parameters given. + e.g., if the expression is "1 + 1", this will return 2.0. + e.g., if the expression is "foo + 1" and parameters contains "foo" = 2, this will return 3.0 +*/ +func (this EvaluableExpression) Eval(parameters Parameters) (interface{}, error) { + + if this.evaluationStages == nil { + return nil, nil + } + + if parameters != nil { + parameters = &sanitizedParameters{parameters} + } + return this.evaluateStage(this.evaluationStages, parameters) +} + +func (this EvaluableExpression) evaluateStage(stage *evaluationStage, parameters Parameters) (interface{}, error) { + + var left, right interface{} + var err error + + if stage.leftStage != nil { + left, err = this.evaluateStage(stage.leftStage, parameters) + if err != nil { + return nil, err + } + } + + if stage.isShortCircuitable() { + switch stage.symbol { + case AND: + if left == false { + return false, nil + } + case OR: + if left == true { + return true, nil + } + case COALESCE: + if left != nil { + return left, nil + } + + case TERNARY_TRUE: + if left == false { + right = shortCircuitHolder + } + case TERNARY_FALSE: + if left != nil { + right = shortCircuitHolder + } + } + } + + if right != shortCircuitHolder && stage.rightStage != nil { + right, err = this.evaluateStage(stage.rightStage, parameters) + if err != nil { + return nil, err + } + } + + if this.ChecksTypes { + if stage.typeCheck == nil { + + err = typeCheck(stage.leftTypeCheck, left, stage.symbol, stage.typeErrorFormat) + if err != nil { + return nil, err + } + + err = typeCheck(stage.rightTypeCheck, right, stage.symbol, stage.typeErrorFormat) + if err != nil { + return nil, err + } + } else { + // special case where the type check needs to know both sides to determine if the operator can handle it + if !stage.typeCheck(left, right) { + errorMsg := fmt.Sprintf(stage.typeErrorFormat, left, stage.symbol.String()) + return nil, errors.New(errorMsg) + } + } + } + + return stage.operator(left, right, parameters) +} + +func typeCheck(check stageTypeCheck, value interface{}, symbol OperatorSymbol, format string) error { + + if check == nil { + return nil + } + + if check(value) { + return nil + } + + errorMsg := fmt.Sprintf(format, value, symbol.String()) + return errors.New(errorMsg) +} + +/* + Returns an array representing the ExpressionTokens that make up this expression. +*/ +func (this EvaluableExpression) Tokens() []ExpressionToken { + + return this.tokens +} + +/* + Returns the original expression used to create this EvaluableExpression. +*/ +func (this EvaluableExpression) String() string { + + return this.inputExpression +} + +/* + Returns an array representing the variables contained in this EvaluableExpression. +*/ +func (this EvaluableExpression) Vars() []string { + var varlist []string + for _, val := range this.Tokens() { + if val.Kind == VARIABLE { + varlist = append(varlist, val.Value.(string)) + } + } + return varlist +} diff --git a/vendor/github.com/Knetic/govaluate/EvaluableExpression_sql.go b/vendor/github.com/Knetic/govaluate/EvaluableExpression_sql.go new file mode 100644 index 000000000..7e0ad1c88 --- /dev/null +++ b/vendor/github.com/Knetic/govaluate/EvaluableExpression_sql.go @@ -0,0 +1,167 @@ +package govaluate + +import ( + "errors" + "fmt" + "regexp" + "time" +) + +/* + Returns a string representing this expression as if it were written in SQL. + This function assumes that all parameters exist within the same table, and that the table essentially represents + a serialized object of some sort (e.g., hibernate). + If your data model is more normalized, you may need to consider iterating through each actual token given by `Tokens()` + to create your query. + + Boolean values are considered to be "1" for true, "0" for false. + + Times are formatted according to this.QueryDateFormat. +*/ +func (this EvaluableExpression) ToSQLQuery() (string, error) { + + var stream *tokenStream + var transactions *expressionOutputStream + var transaction string + var err error + + stream = newTokenStream(this.tokens) + transactions = new(expressionOutputStream) + + for stream.hasNext() { + + transaction, err = this.findNextSQLString(stream, transactions) + if err != nil { + return "", err + } + + transactions.add(transaction) + } + + return transactions.createString(" "), nil +} + +func (this EvaluableExpression) findNextSQLString(stream *tokenStream, transactions *expressionOutputStream) (string, error) { + + var token ExpressionToken + var ret string + + token = stream.next() + + switch token.Kind { + + case STRING: + ret = fmt.Sprintf("'%v'", token.Value) + case PATTERN: + ret = fmt.Sprintf("'%s'", token.Value.(*regexp.Regexp).String()) + case TIME: + ret = fmt.Sprintf("'%s'", token.Value.(time.Time).Format(this.QueryDateFormat)) + + case LOGICALOP: + switch logicalSymbols[token.Value.(string)] { + + case AND: + ret = "AND" + case OR: + ret = "OR" + } + + case BOOLEAN: + if token.Value.(bool) { + ret = "1" + } else { + ret = "0" + } + + case VARIABLE: + ret = fmt.Sprintf("[%s]", token.Value.(string)) + + case NUMERIC: + ret = fmt.Sprintf("%g", token.Value.(float64)) + + case COMPARATOR: + switch comparatorSymbols[token.Value.(string)] { + + case EQ: + ret = "=" + case NEQ: + ret = "<>" + case REQ: + ret = "RLIKE" + case NREQ: + ret = "NOT RLIKE" + default: + ret = fmt.Sprintf("%s", token.Value.(string)) + } + + case TERNARY: + + switch ternarySymbols[token.Value.(string)] { + + case COALESCE: + + left := transactions.rollback() + right, err := this.findNextSQLString(stream, transactions) + if err != nil { + return "", err + } + + ret = fmt.Sprintf("COALESCE(%v, %v)", left, right) + case TERNARY_TRUE: + fallthrough + case TERNARY_FALSE: + return "", errors.New("Ternary operators are unsupported in SQL output") + } + case PREFIX: + switch prefixSymbols[token.Value.(string)] { + + case INVERT: + ret = fmt.Sprintf("NOT") + default: + + right, err := this.findNextSQLString(stream, transactions) + if err != nil { + return "", err + } + + ret = fmt.Sprintf("%s%s", token.Value.(string), right) + } + case MODIFIER: + + switch modifierSymbols[token.Value.(string)] { + + case EXPONENT: + + left := transactions.rollback() + right, err := this.findNextSQLString(stream, transactions) + if err != nil { + return "", err + } + + ret = fmt.Sprintf("POW(%s, %s)", left, right) + case MODULUS: + + left := transactions.rollback() + right, err := this.findNextSQLString(stream, transactions) + if err != nil { + return "", err + } + + ret = fmt.Sprintf("MOD(%s, %s)", left, right) + default: + ret = fmt.Sprintf("%s", token.Value.(string)) + } + case CLAUSE: + ret = "(" + case CLAUSE_CLOSE: + ret = ")" + case SEPARATOR: + ret = "," + + default: + errorMsg := fmt.Sprintf("Unrecognized query token '%s' of kind '%s'", token.Value, token.Kind) + return "", errors.New(errorMsg) + } + + return ret, nil +} diff --git a/vendor/github.com/Knetic/govaluate/ExpressionToken.go b/vendor/github.com/Knetic/govaluate/ExpressionToken.go new file mode 100644 index 000000000..f849f3813 --- /dev/null +++ b/vendor/github.com/Knetic/govaluate/ExpressionToken.go @@ -0,0 +1,9 @@ +package govaluate + +/* + Represents a single parsed token. +*/ +type ExpressionToken struct { + Kind TokenKind + Value interface{} +} diff --git a/vendor/github.com/Knetic/govaluate/LICENSE b/vendor/github.com/Knetic/govaluate/LICENSE new file mode 100644 index 000000000..24b9b4591 --- /dev/null +++ b/vendor/github.com/Knetic/govaluate/LICENSE @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2014-2016 George Lester + +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/Knetic/govaluate/MANUAL.md b/vendor/github.com/Knetic/govaluate/MANUAL.md new file mode 100644 index 000000000..e06582851 --- /dev/null +++ b/vendor/github.com/Knetic/govaluate/MANUAL.md @@ -0,0 +1,176 @@ +govaluate +==== + +This library contains quite a lot of functionality, this document is meant to be formal documentation on the operators and features of it. +Some of this documentation may duplicate what's in README.md, but should never conflict. + +# Types + +This library only officially deals with four types; `float64`, `bool`, `string`, and arrays. + +All numeric literals, with or without a radix, will be converted to `float64` for evaluation. For instance; in practice, there is no difference between the literals "1.0" and "1", they both end up as `float64`. This matters to users because if you intend to return numeric values from your expressions, then the returned value will be `float64`, not any other numeric type. + +Any string _literal_ (not parameter) which is interpretable as a date will be converted to a `float64` representation of that date's unix time. Any `time.Time` parameters will not be operable with these date literals; such parameters will need to use the `time.Time.Unix()` method to get a numeric representation. + +Arrays are untyped, and can be mixed-type. Internally they're all just `interface{}`. Only two operators can interact with arrays, `IN` and `,`. All other operators will refuse to operate on arrays. + +# Operators + +## Modifiers + +### Addition, concatenation `+` + +If either left or right sides of the `+` operator are a `string`, then this operator will perform string concatenation and return that result. If neither are string, then both must be numeric, and this will return a numeric result. + +Any other case is invalid. + +### Arithmetic `-` `*` `/` `**` `%` + +`**` refers to "take to the power of". For instance, `3 ** 4` == 81. + +* _Left side_: numeric +* _Right side_: numeric +* _Returns_: numeric + +### Bitwise shifts, masks `>>` `<<` `|` `&` `^` + +All of these operators convert their `float64` left and right sides to `int64`, perform their operation, and then convert back. +Given how this library assumes numeric are represented (as `float64`), it is unlikely that this behavior will change, even though it may cause havoc with extremely large or small numbers. + +* _Left side_: numeric +* _Right side_: numeric +* _Returns_: numeric + +### Negation `-` + +Prefix only. This can never have a left-hand value. + +* _Right side_: numeric +* _Returns_: numeric + +### Inversion `!` + +Prefix only. This can never have a left-hand value. + +* _Right side_: bool +* _Returns_: bool + +### Bitwise NOT `~` + +Prefix only. This can never have a left-hand value. + +* _Right side_: numeric +* _Returns_: numeric + +## Logical Operators + +For all logical operators, this library will short-circuit the operation if the left-hand side is sufficient to determine what to do. For instance, `true || expensiveOperation()` will not actually call `expensiveOperation()`, since it knows the left-hand side is `true`. + +### Logical AND/OR `&&` `||` + +* _Left side_: bool +* _Right side_: bool +* _Returns_: bool + +### Ternary true `?` + +Checks if the left side is `true`. If so, returns the right side. If the left side is `false`, returns `nil`. +In practice, this is commonly used with the other ternary operator. + +* _Left side_: bool +* _Right side_: Any type. +* _Returns_: Right side or `nil` + +### Ternary false `:` + +Checks if the left side is `nil`. If so, returns the right side. If the left side is non-nil, returns the left side. +In practice, this is commonly used with the other ternary operator. + +* _Left side_: Any type. +* _Right side_: Any type. +* _Returns_: Right side or `nil` + +### Null coalescence `??` + +Similar to the C# operator. If the left value is non-nil, it returns that. If not, then the right-value is returned. + +* _Left side_: Any type. +* _Right side_: Any type. +* _Returns_: No specific type - whichever is passed to it. + +## Comparators + +### Numeric/lexicographic comparators `>` `<` `>=` `<=` + +If both sides are numeric, this returns the usual greater/lesser behavior that would be expected. +If both sides are string, this returns the lexicographic comparison of the strings. This uses Go's standard lexicographic compare. + +* _Accepts_: Left and right side must either be both string, or both numeric. +* _Returns_: bool + +### Regex comparators `=~` `!~` + +These use go's standard `regexp` flavor of regex. The left side is expected to be the candidate string, the right side is the pattern. `=~` returns whether or not the candidate string matches the regex pattern given on the right. `!~` is the inverted version of the same logic. + +* _Left side_: string +* _Right side_: string +* _Returns_: bool + +## Arrays + +### Separator `,` + +The separator, always paired with parenthesis, creates arrays. It must always have both a left and right-hand value, so for instance `(, 0)` and `(0,)` are invalid uses of it. + +Again, this should always be used with parenthesis; like `(1, 2, 3, 4)`. + +### Membership `IN` + +The only operator with a text name, this operator checks the right-hand side array to see if it contains a value that is equal to the left-side value. +Equality is determined by the use of the `==` operator, and this library doesn't check types between the values. Any two values, when cast to `interface{}`, and can still be checked for equality with `==` will act as expected. + +Note that you can use a parameter for the array, but it must be an `[]interface{}`. + +* _Left side_: Any type. +* _Right side_: array +* _Returns_: bool + +# Parameters + +Parameters must be passed in every time the expression is evaluated. Parameters can be of any type, but will not cause errors unless actually used in an erroneous way. There is no difference in behavior for any of the above operators for parameters - they are type checked when used. + +All `int` and `float` values of any width will be converted to `float64` before use. + +At no point is the parameter structure, or any value thereof, modified by this library. + +## Alternates to maps + +The default form of parameters as a map may not serve your use case. You may have parameters in some other structure, you may want to change the no-parameter-found behavior, or maybe even just have some debugging print statements invoked when a parameter is accessed. + +To do this, define a type that implements the `govaluate.Parameters` interface. When you want to evaluate, instead call `EvaluableExpression.Eval` and pass your parameter structure. + +# Functions + +During expression parsing (_not_ evaluation), a map of functions can be given to `govaluate.NewEvaluableExpressionWithFunctions` (the lengthiest and finest of function names). The resultant expression will be able to invoke those functions during evaluation. Once parsed, an expression cannot have functions added or removed - a new expression will need to be created if you want to change the functions, or behavior of said functions. + +Functions always take the form `()`, including parens. Functions can have an empty list of parameters, like `()`, but still must have parens. + +If the expression contains something that looks like it ought to be a function (such as `foo()`), but no such function was given to it, it will error on parsing. + +Functions must be of type `map[string]govaluate.ExpressionFunction`. `ExpressionFunction`, for brevity, has the following signature: + +`func(args ...interface{}) (interface{}, error)` + +Where `args` is whatever is passed to the function when called. If a non-nil error is returned from a function during evaluation, the evaluation stops and ultimately returns that error to the caller of `Evaluate()` or `Eval()`. + +## Built-in functions + +There aren't any builtin functions. The author is opposed to maintaining a standard library of functions to be used. + +Every use case of this library is different, and even in simple use cases (such as parameters, see above) different users need different behavior, naming, or even functionality. The author prefers that users make their own decisions about what functions they need, and how they operate. + +# Equality + +The `==` and `!=` operators involve a moderately complex workflow. They use [`reflect.DeepEqual`](https://golang.org/pkg/reflect/#DeepEqual). This is for complicated reasons, but there are some types in Go that cannot be compared with the native `==` operator. Arrays, in particular, cannot be compared - Go will panic if you try. One might assume this could be handled with the type checking system in `govaluate`, but unfortunately without reflection there is no way to know if a variable is a slice/array. Worse, structs can be incomparable if they _contain incomparable types_. + +It's all very complicated. Fortunately, Go includes the `reflect.DeepEqual` function to handle all the edge cases. Currently, `govaluate` uses that for all equality/inequality. diff --git a/vendor/github.com/Knetic/govaluate/OperatorSymbol.go b/vendor/github.com/Knetic/govaluate/OperatorSymbol.go new file mode 100644 index 000000000..48813d291 --- /dev/null +++ b/vendor/github.com/Knetic/govaluate/OperatorSymbol.go @@ -0,0 +1,306 @@ +package govaluate + +/* + Represents the valid symbols for operators. + +*/ +type OperatorSymbol int + +const ( + VALUE OperatorSymbol = iota + LITERAL + NOOP + EQ + NEQ + GT + LT + GTE + LTE + REQ + NREQ + IN + + AND + OR + + PLUS + MINUS + BITWISE_AND + BITWISE_OR + BITWISE_XOR + BITWISE_LSHIFT + BITWISE_RSHIFT + MULTIPLY + DIVIDE + MODULUS + EXPONENT + + NEGATE + INVERT + BITWISE_NOT + + TERNARY_TRUE + TERNARY_FALSE + COALESCE + + FUNCTIONAL + SEPARATE +) + +type operatorPrecedence int + +const ( + noopPrecedence operatorPrecedence = iota + valuePrecedence + functionalPrecedence + prefixPrecedence + exponentialPrecedence + additivePrecedence + bitwisePrecedence + bitwiseShiftPrecedence + multiplicativePrecedence + comparatorPrecedence + ternaryPrecedence + logicalAndPrecedence + logicalOrPrecedence + separatePrecedence +) + +func findOperatorPrecedenceForSymbol(symbol OperatorSymbol) operatorPrecedence { + + switch symbol { + case NOOP: + return noopPrecedence + case VALUE: + return valuePrecedence + case EQ: + fallthrough + case NEQ: + fallthrough + case GT: + fallthrough + case LT: + fallthrough + case GTE: + fallthrough + case LTE: + fallthrough + case REQ: + fallthrough + case NREQ: + fallthrough + case IN: + return comparatorPrecedence + case AND: + return logicalAndPrecedence + case OR: + return logicalOrPrecedence + case BITWISE_AND: + fallthrough + case BITWISE_OR: + fallthrough + case BITWISE_XOR: + return bitwisePrecedence + case BITWISE_LSHIFT: + fallthrough + case BITWISE_RSHIFT: + return bitwiseShiftPrecedence + case PLUS: + fallthrough + case MINUS: + return additivePrecedence + case MULTIPLY: + fallthrough + case DIVIDE: + fallthrough + case MODULUS: + return multiplicativePrecedence + case EXPONENT: + return exponentialPrecedence + case BITWISE_NOT: + fallthrough + case NEGATE: + fallthrough + case INVERT: + return prefixPrecedence + case COALESCE: + fallthrough + case TERNARY_TRUE: + fallthrough + case TERNARY_FALSE: + return ternaryPrecedence + case FUNCTIONAL: + return functionalPrecedence + case SEPARATE: + return separatePrecedence + } + + return valuePrecedence +} + +/* + Map of all valid comparators, and their string equivalents. + Used during parsing of expressions to determine if a symbol is, in fact, a comparator. + Also used during evaluation to determine exactly which comparator is being used. +*/ +var comparatorSymbols = map[string]OperatorSymbol{ + "==": EQ, + "!=": NEQ, + ">": GT, + ">=": GTE, + "<": LT, + "<=": LTE, + "=~": REQ, + "!~": NREQ, + "in": IN, +} + +var logicalSymbols = map[string]OperatorSymbol{ + "&&": AND, + "||": OR, +} + +var bitwiseSymbols = map[string]OperatorSymbol{ + "^": BITWISE_XOR, + "&": BITWISE_AND, + "|": BITWISE_OR, +} + +var bitwiseShiftSymbols = map[string]OperatorSymbol{ + ">>": BITWISE_RSHIFT, + "<<": BITWISE_LSHIFT, +} + +var additiveSymbols = map[string]OperatorSymbol{ + "+": PLUS, + "-": MINUS, +} + +var multiplicativeSymbols = map[string]OperatorSymbol{ + "*": MULTIPLY, + "/": DIVIDE, + "%": MODULUS, +} + +var exponentialSymbolsS = map[string]OperatorSymbol{ + "**": EXPONENT, +} + +var prefixSymbols = map[string]OperatorSymbol{ + "-": NEGATE, + "!": INVERT, + "~": BITWISE_NOT, +} + +var ternarySymbols = map[string]OperatorSymbol{ + "?": TERNARY_TRUE, + ":": TERNARY_FALSE, + "??": COALESCE, +} + +// this is defined separately from additiveSymbols et al because it's needed for parsing, not stage planning. +var modifierSymbols = map[string]OperatorSymbol{ + "+": PLUS, + "-": MINUS, + "*": MULTIPLY, + "/": DIVIDE, + "%": MODULUS, + "**": EXPONENT, + "&": BITWISE_AND, + "|": BITWISE_OR, + "^": BITWISE_XOR, + ">>": BITWISE_RSHIFT, + "<<": BITWISE_LSHIFT, +} + +var separatorSymbols = map[string]OperatorSymbol{ + ",": SEPARATE, +} + +/* + Returns true if this operator is contained by the given array of candidate symbols. + False otherwise. +*/ +func (this OperatorSymbol) IsModifierType(candidate []OperatorSymbol) bool { + + for _, symbolType := range candidate { + if this == symbolType { + return true + } + } + + return false +} + +/* + Generally used when formatting type check errors. + We could store the stringified symbol somewhere else and not require a duplicated codeblock to translate + OperatorSymbol to string, but that would require more memory, and another field somewhere. + Adding operators is rare enough that we just stringify it here instead. +*/ +func (this OperatorSymbol) String() string { + + switch this { + case NOOP: + return "NOOP" + case VALUE: + return "VALUE" + case EQ: + return "=" + case NEQ: + return "!=" + case GT: + return ">" + case LT: + return "<" + case GTE: + return ">=" + case LTE: + return "<=" + case REQ: + return "=~" + case NREQ: + return "!~" + case AND: + return "&&" + case OR: + return "||" + case IN: + return "in" + case BITWISE_AND: + return "&" + case BITWISE_OR: + return "|" + case BITWISE_XOR: + return "^" + case BITWISE_LSHIFT: + return "<<" + case BITWISE_RSHIFT: + return ">>" + case PLUS: + return "+" + case MINUS: + return "-" + case MULTIPLY: + return "*" + case DIVIDE: + return "/" + case MODULUS: + return "%" + case EXPONENT: + return "**" + case NEGATE: + return "-" + case INVERT: + return "!" + case BITWISE_NOT: + return "~" + case TERNARY_TRUE: + return "?" + case TERNARY_FALSE: + return ":" + case COALESCE: + return "??" + } + return "" +} diff --git a/vendor/github.com/Knetic/govaluate/README.md b/vendor/github.com/Knetic/govaluate/README.md new file mode 100644 index 000000000..42768e6da --- /dev/null +++ b/vendor/github.com/Knetic/govaluate/README.md @@ -0,0 +1,210 @@ +govaluate +==== + +[![Build Status](https://travis-ci.org/Knetic/govaluate.svg?branch=master)](https://travis-ci.org/Knetic/govaluate) +[![Godoc](https://godoc.org/github.com/Knetic/govaluate?status.png)](https://godoc.org/github.com/Knetic/govaluate) + + +Provides support for evaluating arbitrary C-like artithmetic/string expressions. + +Why can't you just write these expressions in code? +-- + +Sometimes, you can't know ahead-of-time what an expression will look like, or you want those expressions to be configurable. +Perhaps you've got a set of data running through your application, and you want to allow your users to specify some validations to run on it before committing it to a database. Or maybe you've written a monitoring framework which is capable of gathering a bunch of metrics, then evaluating a few expressions to see if any metrics should be alerted upon, but the conditions for alerting are different for each monitor. + +A lot of people wind up writing their own half-baked style of evaluation language that fits their needs, but isn't complete. Or they wind up baking the expression into the actual executable, even if they know it's subject to change. These strategies may work, but they take time to implement, time for users to learn, and induce technical debt as requirements change. This library is meant to cover all the normal C-like expressions, so that you don't have to reinvent one of the oldest wheels on a computer. + +How do I use it? +-- + +You create a new EvaluableExpression, then call "Evaluate" on it. + +```go + expression, err := govaluate.NewEvaluableExpression("10 > 0"); + result, err := expression.Evaluate(nil); + // result is now set to "true", the bool value. +``` + +Cool, but how about with parameters? + +```go + expression, err := govaluate.NewEvaluableExpression("foo > 0"); + + parameters := make(map[string]interface{}, 8) + parameters["foo"] = -1; + + result, err := expression.Evaluate(parameters); + // result is now set to "false", the bool value. +``` + +That's cool, but we can almost certainly have done all that in code. What about a complex use case that involves some math? + +```go + expression, err := govaluate.NewEvaluableExpression("(requests_made * requests_succeeded / 100) >= 90"); + + parameters := make(map[string]interface{}, 8) + parameters["requests_made"] = 100; + parameters["requests_succeeded"] = 80; + + result, err := expression.Evaluate(parameters); + // result is now set to "false", the bool value. +``` + +Or maybe you want to check the status of an alive check ("smoketest") page, which will be a string? + +```go + expression, err := govaluate.NewEvaluableExpression("http_response_body == 'service is ok'"); + + parameters := make(map[string]interface{}, 8) + parameters["http_response_body"] = "service is ok"; + + result, err := expression.Evaluate(parameters); + // result is now set to "true", the bool value. +``` + +These examples have all returned boolean values, but it's equally possible to return numeric ones. + +```go + expression, err := govaluate.NewEvaluableExpression("(mem_used / total_mem) * 100"); + + parameters := make(map[string]interface{}, 8) + parameters["total_mem"] = 1024; + parameters["mem_used"] = 512; + + result, err := expression.Evaluate(parameters); + // result is now set to "50.0", the float64 value. +``` + +You can also do date parsing, though the formats are somewhat limited. Stick to RF3339, ISO8061, unix date, or ruby date formats. If you're having trouble getting a date string to parse, check the list of formats actually used: [parsing.go:248](https://github.com/Knetic/govaluate/blob/0580e9b47a69125afa0e4ebd1cf93c49eb5a43ec/parsing.go#L258). + +```go + expression, err := govaluate.NewEvaluableExpression("'2014-01-02' > '2014-01-01 23:59:59'"); + result, err := expression.Evaluate(nil); + + // result is now set to true +``` + +Expressions are parsed once, and can be re-used multiple times. Parsing is the compute-intensive phase of the process, so if you intend to use the same expression with different parameters, just parse it once. Like so; + +```go + expression, err := govaluate.NewEvaluableExpression("response_time <= 100"); + parameters := make(map[string]interface{}, 8) + + for { + parameters["response_time"] = pingSomething(); + result, err := expression.Evaluate(parameters) + } +``` + +The normal C-standard order of operators is respected. When writing an expression, be sure that you either order the operators correctly, or use parenthesis to clarify which portions of an expression should be run first. + +Escaping characters +-- + +Sometimes you'll have parameters that have spaces, slashes, pluses, ampersands or some other character +that this library interprets as something special. For example, the following expression will not +act as one might expect: + + "response-time < 100" + +As written, the library will parse it as "[response] minus [time] is less than 100". In reality, +"response-time" is meant to be one variable that just happens to have a dash in it. + +There are two ways to work around this. First, you can escape the entire parameter name: + + "[response-time] < 100" + +Or you can use backslashes to escape only the minus sign. + + "response\\-time < 100" + +Backslashes can be used anywhere in an expression to escape the very next character. Square bracketed parameter names can be used instead of plain parameter names at any time. + +Functions +-- + +You may have cases where you want to call a function on a parameter during execution of the expression. Perhaps you want to aggregate some set of data, but don't know the exact aggregation you want to use until you're writing the expression itself. Or maybe you have a mathematical operation you want to perform, for which there is no operator; like `log` or `tan` or `sqrt`. For cases like this, you can provide a map of functions to `NewEvaluableExpressionWithFunctions`, which will then be able to use them during execution. For instance; + +```go + functions := map[string]govaluate.ExpressionFunction { + "strlen": func(args ...interface{}) (interface{}, error) { + length := len(args[0].(string)) + return (float64)(length), nil + }, + } + + expString := "strlen('someReallyLongInputString') <= 16" + expression, _ := govaluate.NewEvaluableExpressionWithFunctions(expString, functions) + + result, _ := expression.Evaluate(nil) + // result is now "false", the boolean value +``` + +Functions can accept any number of arguments, correctly handles nested functions, and arguments can be of any type (even if none of this library's operators support evaluation of that type). For instance, each of these usages of functions in an expression are valid (assuming that the appropriate functions and parameters are given): + +```go +"sqrt(x1 ** y1, x2 ** y2)" +"max(someValue, abs(anotherValue), 10 * lastValue)" +``` + +Functions cannot be passed as parameters, they must be known at the time when the expression is parsed, and are unchangeable after parsing. + +What operators and types does this support? +-- + +* Modifiers: `+` `-` `/` `*` `&` `|` `^` `**` `%` `>>` `<<` +* Comparators: `>` `>=` `<` `<=` `==` `!=` `=~` `!~` +* Logical ops: `||` `&&` +* Numeric constants, as 64-bit floating point (`12345.678`) +* String constants (single quotes: `'foobar'`) +* Date constants (single quotes, using any permutation of RFC3339, ISO8601, ruby date, or unix date; date parsing is automatically tried with any string constant) +* Boolean constants: `true` `false` +* Parenthesis to control order of evaluation `(` `)` +* Arrays (anything separated by `,` within parenthesis: `(1, 2, 'foo')`) +* Prefixes: `!` `-` `~` +* Ternary conditional: `?` `:` +* Null coalescence: `??` + +See [MANUAL.md](https://github.com/Knetic/govaluate/blob/master/MANUAL.md) for exacting details on what types each operator supports. + +Types +-- + +Some operators don't make sense when used with some types. For instance, what does it mean to get the modulo of a string? What happens if you check to see if two numbers are logically AND'ed together? + +Everyone has a different intuition about the answers to these questions. To prevent confusion, this library will _refuse to operate_ upon types for which there is not an unambiguous meaning for the operation. See [MANUAL.md](https://github.com/Knetic/govaluate/blob/master/MANUAL.md) for details about what operators are valid for which types. + +Benchmarks +-- + +If you're concerned about the overhead of this library, a good range of benchmarks are built into this repo. You can run them with `go test -bench=.`. The library is built with an eye towards being quick, but has not been aggressively profiled and optimized. For most applications, though, it is completely fine. + +For a very rough idea of performance, here are the results output from a benchmark run on a 3rd-gen Macbook Pro (Linux Mint 17.1). + +``` +BenchmarkSingleParse-12 1000000 1382 ns/op +BenchmarkSimpleParse-12 200000 10771 ns/op +BenchmarkFullParse-12 30000 49383 ns/op +BenchmarkEvaluationSingle-12 50000000 30.1 ns/op +BenchmarkEvaluationNumericLiteral-12 10000000 119 ns/op +BenchmarkEvaluationLiteralModifiers-12 10000000 236 ns/op +BenchmarkEvaluationParameters-12 5000000 260 ns/op +BenchmarkEvaluationParametersModifiers-12 3000000 547 ns/op +BenchmarkComplexExpression-12 2000000 963 ns/op +BenchmarkRegexExpression-12 100000 20357 ns/op +BenchmarkConstantRegexExpression-12 1000000 1392 ns/op +ok +``` + +API Breaks +-- + +While this library has very few cases which will ever result in an API break, it can (and [has](https://github.com/Knetic/govaluate/releases/tag/2.0.0)) happened. If you are using this in production, vendor the commit you've tested against, or use gopkg.in to redirect your import (e.g., `import "gopkg.in/Knetic/govaluate.v2"`). Master branch (while infrequent) _may_ at some point contain API breaking changes, and the author will have no way to communicate these to downstreams, other than creating a new major release. + +Releases will explicitly state when an API break happens, and if they do not specify an API break it should be safe to upgrade. + +License +-- + +This project is licensed under the MIT general use license. You're free to integrate, fork, and play with this code as you feel fit without consulting the author, as long as you provide proper credit to the author in your works. diff --git a/vendor/github.com/Knetic/govaluate/TokenKind.go b/vendor/github.com/Knetic/govaluate/TokenKind.go new file mode 100644 index 000000000..b7355bb74 --- /dev/null +++ b/vendor/github.com/Knetic/govaluate/TokenKind.go @@ -0,0 +1,72 @@ +package govaluate + +/* + Represents all valid types of tokens that a token can be. +*/ +type TokenKind int + +const ( + UNKNOWN TokenKind = iota + + PREFIX + NUMERIC + BOOLEAN + STRING + PATTERN + TIME + VARIABLE + FUNCTION + SEPARATOR + + COMPARATOR + LOGICALOP + MODIFIER + + CLAUSE + CLAUSE_CLOSE + + TERNARY +) + +/* + GetTokenKindString returns a string that describes the given TokenKind. + e.g., when passed the NUMERIC TokenKind, this returns the string "NUMERIC". +*/ +func (kind TokenKind) String() string { + + switch kind { + + case PREFIX: + return "PREFIX" + case NUMERIC: + return "NUMERIC" + case BOOLEAN: + return "BOOLEAN" + case STRING: + return "STRING" + case PATTERN: + return "PATTERN" + case TIME: + return "TIME" + case VARIABLE: + return "VARIABLE" + case FUNCTION: + return "FUNCTION" + case SEPARATOR: + return "SEPARATOR" + case COMPARATOR: + return "COMPARATOR" + case LOGICALOP: + return "LOGICALOP" + case MODIFIER: + return "MODIFIER" + case CLAUSE: + return "CLAUSE" + case CLAUSE_CLOSE: + return "CLAUSE_CLOSE" + case TERNARY: + return "TERNARY" + } + + return "UNKNOWN" +} diff --git a/vendor/github.com/Knetic/govaluate/evaluationStage.go b/vendor/github.com/Knetic/govaluate/evaluationStage.go new file mode 100644 index 000000000..bb6688199 --- /dev/null +++ b/vendor/github.com/Knetic/govaluate/evaluationStage.go @@ -0,0 +1,359 @@ +package govaluate + +import ( + "errors" + "fmt" + "math" + "regexp" + "reflect" +) + +const ( + logicalErrorFormat string = "Value '%v' cannot be used with the logical operator '%v', it is not a bool" + modifierErrorFormat string = "Value '%v' cannot be used with the modifier '%v', it is not a number" + comparatorErrorFormat string = "Value '%v' cannot be used with the comparator '%v', it is not a number" + ternaryErrorFormat string = "Value '%v' cannot be used with the ternary operator '%v', it is not a bool" + prefixErrorFormat string = "Value '%v' cannot be used with the prefix '%v'" +) + +type evaluationOperator func(left interface{}, right interface{}, parameters Parameters) (interface{}, error) +type stageTypeCheck func(value interface{}) bool +type stageCombinedTypeCheck func(left interface{}, right interface{}) bool + +type evaluationStage struct { + symbol OperatorSymbol + + leftStage, rightStage *evaluationStage + + // the operation that will be used to evaluate this stage (such as adding [left] to [right] and return the result) + operator evaluationOperator + + // ensures that both left and right values are appropriate for this stage. Returns an error if they aren't operable. + leftTypeCheck stageTypeCheck + rightTypeCheck stageTypeCheck + + // if specified, will override whatever is used in "leftTypeCheck" and "rightTypeCheck". + // primarily used for specific operators that don't care which side a given type is on, but still requires one side to be of a given type + // (like string concat) + typeCheck stageCombinedTypeCheck + + // regardless of which type check is used, this string format will be used as the error message for type errors + typeErrorFormat string +} + +var ( + _true = interface{}(true) + _false = interface{}(false) +) + +func (this *evaluationStage) swapWith(other *evaluationStage) { + + temp := *other + other.setToNonStage(*this) + this.setToNonStage(temp) +} + +func (this *evaluationStage) setToNonStage(other evaluationStage) { + + this.symbol = other.symbol + this.operator = other.operator + this.leftTypeCheck = other.leftTypeCheck + this.rightTypeCheck = other.rightTypeCheck + this.typeCheck = other.typeCheck + this.typeErrorFormat = other.typeErrorFormat +} + +func (this *evaluationStage) isShortCircuitable() bool { + + switch this.symbol { + case AND: + fallthrough + case OR: + fallthrough + case TERNARY_TRUE: + fallthrough + case TERNARY_FALSE: + fallthrough + case COALESCE: + return true + } + + return false +} + +func noopStageRight(left interface{}, right interface{}, parameters Parameters) (interface{}, error) { + return right, nil +} + +func addStage(left interface{}, right interface{}, parameters Parameters) (interface{}, error) { + + // string concat if either are strings + if isString(left) || isString(right) { + return fmt.Sprintf("%v%v", left, right), nil + } + + return left.(float64) + right.(float64), nil +} +func subtractStage(left interface{}, right interface{}, parameters Parameters) (interface{}, error) { + return left.(float64) - right.(float64), nil +} +func multiplyStage(left interface{}, right interface{}, parameters Parameters) (interface{}, error) { + return left.(float64) * right.(float64), nil +} +func divideStage(left interface{}, right interface{}, parameters Parameters) (interface{}, error) { + return left.(float64) / right.(float64), nil +} +func exponentStage(left interface{}, right interface{}, parameters Parameters) (interface{}, error) { + return math.Pow(left.(float64), right.(float64)), nil +} +func modulusStage(left interface{}, right interface{}, parameters Parameters) (interface{}, error) { + return math.Mod(left.(float64), right.(float64)), nil +} +func gteStage(left interface{}, right interface{}, parameters Parameters) (interface{}, error) { + if isString(left) && isString(right) { + return boolIface(left.(string) >= right.(string)), nil + } + return boolIface(left.(float64) >= right.(float64)), nil +} +func gtStage(left interface{}, right interface{}, parameters Parameters) (interface{}, error) { + if isString(left) && isString(right) { + return boolIface(left.(string) > right.(string)), nil + } + return boolIface(left.(float64) > right.(float64)), nil +} +func lteStage(left interface{}, right interface{}, parameters Parameters) (interface{}, error) { + if isString(left) && isString(right) { + return boolIface(left.(string) <= right.(string)), nil + } + return boolIface(left.(float64) <= right.(float64)), nil +} +func ltStage(left interface{}, right interface{}, parameters Parameters) (interface{}, error) { + if isString(left) && isString(right) { + return boolIface(left.(string) < right.(string)), nil + } + return boolIface(left.(float64) < right.(float64)), nil +} +func equalStage(left interface{}, right interface{}, parameters Parameters) (interface{}, error) { + return boolIface(reflect.DeepEqual(left, right)), nil +} +func notEqualStage(left interface{}, right interface{}, parameters Parameters) (interface{}, error) { + return boolIface(!reflect.DeepEqual(left, right)), nil +} +func andStage(left interface{}, right interface{}, parameters Parameters) (interface{}, error) { + return boolIface(left.(bool) && right.(bool)), nil +} +func orStage(left interface{}, right interface{}, parameters Parameters) (interface{}, error) { + return boolIface(left.(bool) || right.(bool)), nil +} +func negateStage(left interface{}, right interface{}, parameters Parameters) (interface{}, error) { + return -right.(float64), nil +} +func invertStage(left interface{}, right interface{}, parameters Parameters) (interface{}, error) { + return boolIface(!right.(bool)), nil +} +func bitwiseNotStage(left interface{}, right interface{}, parameters Parameters) (interface{}, error) { + return float64(^int64(right.(float64))), nil +} +func ternaryIfStage(left interface{}, right interface{}, parameters Parameters) (interface{}, error) { + if left.(bool) { + return right, nil + } + return nil, nil +} +func ternaryElseStage(left interface{}, right interface{}, parameters Parameters) (interface{}, error) { + if left != nil { + return left, nil + } + return right, nil +} + +func regexStage(left interface{}, right interface{}, parameters Parameters) (interface{}, error) { + + var pattern *regexp.Regexp + var err error + + switch right.(type) { + case string: + pattern, err = regexp.Compile(right.(string)) + if err != nil { + return nil, errors.New(fmt.Sprintf("Unable to compile regexp pattern '%v': %v", right, err)) + } + case *regexp.Regexp: + pattern = right.(*regexp.Regexp) + } + + return pattern.Match([]byte(left.(string))), nil +} + +func notRegexStage(left interface{}, right interface{}, parameters Parameters) (interface{}, error) { + + ret, err := regexStage(left, right, parameters) + if err != nil { + return nil, err + } + + return !(ret.(bool)), nil +} + +func bitwiseOrStage(left interface{}, right interface{}, parameters Parameters) (interface{}, error) { + return float64(int64(left.(float64)) | int64(right.(float64))), nil +} +func bitwiseAndStage(left interface{}, right interface{}, parameters Parameters) (interface{}, error) { + return float64(int64(left.(float64)) & int64(right.(float64))), nil +} +func bitwiseXORStage(left interface{}, right interface{}, parameters Parameters) (interface{}, error) { + return float64(int64(left.(float64)) ^ int64(right.(float64))), nil +} +func leftShiftStage(left interface{}, right interface{}, parameters Parameters) (interface{}, error) { + return float64(uint64(left.(float64)) << uint64(right.(float64))), nil +} +func rightShiftStage(left interface{}, right interface{}, parameters Parameters) (interface{}, error) { + return float64(uint64(left.(float64)) >> uint64(right.(float64))), nil +} + +func makeParameterStage(parameterName string) evaluationOperator { + + return func(left interface{}, right interface{}, parameters Parameters) (interface{}, error) { + value, err := parameters.Get(parameterName) + if err != nil { + return nil, err + } + + return value, nil + } +} + +func makeLiteralStage(literal interface{}) evaluationOperator { + return func(left interface{}, right interface{}, parameters Parameters) (interface{}, error) { + return literal, nil + } +} + +func makeFunctionStage(function ExpressionFunction) evaluationOperator { + + return func(left interface{}, right interface{}, parameters Parameters) (interface{}, error) { + + if right == nil { + return function() + } + + switch right.(type) { + case []interface{}: + return function(right.([]interface{})...) + default: + return function(right) + } + + } +} + +func separatorStage(left interface{}, right interface{}, parameters Parameters) (interface{}, error) { + + var ret []interface{} + + switch left.(type) { + case []interface{}: + ret = append(left.([]interface{}), right) + default: + ret = []interface{}{left, right} + } + + return ret, nil +} + +func inStage(left interface{}, right interface{}, parameters Parameters) (interface{}, error) { + + for _, value := range right.([]interface{}) { + if left == value { + return true, nil + } + } + return false, nil +} + +// + +func isString(value interface{}) bool { + + switch value.(type) { + case string: + return true + } + return false +} + +func isRegexOrString(value interface{}) bool { + + switch value.(type) { + case string: + return true + case *regexp.Regexp: + return true + } + return false +} + +func isBool(value interface{}) bool { + switch value.(type) { + case bool: + return true + } + return false +} + +func isFloat64(value interface{}) bool { + switch value.(type) { + case float64: + return true + } + return false +} + +/* + Addition usually means between numbers, but can also mean string concat. + String concat needs one (or both) of the sides to be a string. +*/ +func additionTypeCheck(left interface{}, right interface{}) bool { + + if isFloat64(left) && isFloat64(right) { + return true + } + if !isString(left) && !isString(right) { + return false + } + return true +} + +/* + Comparison can either be between numbers, or lexicographic between two strings, + but never between the two. +*/ +func comparatorTypeCheck(left interface{}, right interface{}) bool { + + if isFloat64(left) && isFloat64(right) { + return true + } + if isString(left) && isString(right) { + return true + } + return false +} + +func isArray(value interface{}) bool { + switch value.(type) { + case []interface{}: + return true + } + return false +} + +/* + Converting a boolean to an interface{} requires an allocation. + We can use interned bools to avoid this cost. +*/ +func boolIface(b bool) interface{} { + if b { + return _true + } + return _false +} diff --git a/vendor/github.com/Knetic/govaluate/expressionFunctions.go b/vendor/github.com/Knetic/govaluate/expressionFunctions.go new file mode 100644 index 000000000..ac6592b3f --- /dev/null +++ b/vendor/github.com/Knetic/govaluate/expressionFunctions.go @@ -0,0 +1,8 @@ +package govaluate + +/* + Represents a function that can be called from within an expression. + This method must return an error if, for any reason, it is unable to produce exactly one unambiguous result. + An error returned will halt execution of the expression. +*/ +type ExpressionFunction func(arguments ...interface{}) (interface{}, error) diff --git a/vendor/github.com/Knetic/govaluate/expressionOutputStream.go b/vendor/github.com/Knetic/govaluate/expressionOutputStream.go new file mode 100644 index 000000000..88a841639 --- /dev/null +++ b/vendor/github.com/Knetic/govaluate/expressionOutputStream.go @@ -0,0 +1,46 @@ +package govaluate + +import ( + "bytes" +) + +/* + Holds a series of "transactions" which represent each token as it is output by an outputter (such as ToSQLQuery()). + Some outputs (such as SQL) require a function call or non-c-like syntax to represent an expression. + To accomplish this, this struct keeps track of each translated token as it is output, and can return and rollback those transactions. +*/ +type expressionOutputStream struct { + transactions []string +} + +func (this *expressionOutputStream) add(transaction string) { + this.transactions = append(this.transactions, transaction) +} + +func (this *expressionOutputStream) rollback() string { + + index := len(this.transactions) - 1 + ret := this.transactions[index] + + this.transactions = this.transactions[:index] + return ret +} + +func (this *expressionOutputStream) createString(delimiter string) string { + + var retBuffer bytes.Buffer + var transaction string + + penultimate := len(this.transactions) - 1 + + for i := 0; i < penultimate; i++ { + + transaction = this.transactions[i] + + retBuffer.WriteString(transaction) + retBuffer.WriteString(delimiter) + } + retBuffer.WriteString(this.transactions[penultimate]) + + return retBuffer.String() +} diff --git a/vendor/github.com/Knetic/govaluate/lexerState.go b/vendor/github.com/Knetic/govaluate/lexerState.go new file mode 100644 index 000000000..9d43760ed --- /dev/null +++ b/vendor/github.com/Knetic/govaluate/lexerState.go @@ -0,0 +1,350 @@ +package govaluate + +import ( + "errors" + "fmt" +) + +type lexerState struct { + isEOF bool + isNullable bool + kind TokenKind + validNextKinds []TokenKind +} + +// lexer states. +// Constant for all purposes except compiler. +var validLexerStates = []lexerState{ + + lexerState{ + kind: UNKNOWN, + isEOF: false, + isNullable: true, + validNextKinds: []TokenKind{ + + PREFIX, + NUMERIC, + BOOLEAN, + VARIABLE, + PATTERN, + FUNCTION, + STRING, + TIME, + CLAUSE, + }, + }, + + lexerState{ + + kind: CLAUSE, + isEOF: false, + isNullable: true, + validNextKinds: []TokenKind{ + + PREFIX, + NUMERIC, + BOOLEAN, + VARIABLE, + PATTERN, + FUNCTION, + STRING, + TIME, + CLAUSE, + CLAUSE_CLOSE, + }, + }, + + lexerState{ + + kind: CLAUSE_CLOSE, + isEOF: true, + isNullable: true, + validNextKinds: []TokenKind{ + + COMPARATOR, + MODIFIER, + NUMERIC, + BOOLEAN, + VARIABLE, + STRING, + PATTERN, + TIME, + CLAUSE, + CLAUSE_CLOSE, + LOGICALOP, + TERNARY, + SEPARATOR, + }, + }, + + lexerState{ + + kind: NUMERIC, + isEOF: true, + isNullable: false, + validNextKinds: []TokenKind{ + + MODIFIER, + COMPARATOR, + LOGICALOP, + CLAUSE_CLOSE, + TERNARY, + SEPARATOR, + }, + }, + lexerState{ + + kind: BOOLEAN, + isEOF: true, + isNullable: false, + validNextKinds: []TokenKind{ + + MODIFIER, + COMPARATOR, + LOGICALOP, + CLAUSE_CLOSE, + TERNARY, + SEPARATOR, + }, + }, + lexerState{ + + kind: STRING, + isEOF: true, + isNullable: false, + validNextKinds: []TokenKind{ + + MODIFIER, + COMPARATOR, + LOGICALOP, + CLAUSE_CLOSE, + TERNARY, + SEPARATOR, + }, + }, + lexerState{ + + kind: TIME, + isEOF: true, + isNullable: false, + validNextKinds: []TokenKind{ + + MODIFIER, + COMPARATOR, + LOGICALOP, + CLAUSE_CLOSE, + SEPARATOR, + }, + }, + lexerState{ + + kind: PATTERN, + isEOF: true, + isNullable: false, + validNextKinds: []TokenKind{ + + MODIFIER, + COMPARATOR, + LOGICALOP, + CLAUSE_CLOSE, + SEPARATOR, + }, + }, + lexerState{ + + kind: VARIABLE, + isEOF: true, + isNullable: false, + validNextKinds: []TokenKind{ + + MODIFIER, + COMPARATOR, + LOGICALOP, + CLAUSE_CLOSE, + TERNARY, + SEPARATOR, + }, + }, + lexerState{ + + kind: MODIFIER, + isEOF: false, + isNullable: false, + validNextKinds: []TokenKind{ + + PREFIX, + NUMERIC, + VARIABLE, + FUNCTION, + STRING, + BOOLEAN, + CLAUSE, + CLAUSE_CLOSE, + }, + }, + lexerState{ + + kind: COMPARATOR, + isEOF: false, + isNullable: false, + validNextKinds: []TokenKind{ + + PREFIX, + NUMERIC, + BOOLEAN, + VARIABLE, + FUNCTION, + STRING, + TIME, + CLAUSE, + CLAUSE_CLOSE, + PATTERN, + }, + }, + lexerState{ + + kind: LOGICALOP, + isEOF: false, + isNullable: false, + validNextKinds: []TokenKind{ + + PREFIX, + NUMERIC, + BOOLEAN, + VARIABLE, + FUNCTION, + STRING, + TIME, + CLAUSE, + CLAUSE_CLOSE, + }, + }, + lexerState{ + + kind: PREFIX, + isEOF: false, + isNullable: false, + validNextKinds: []TokenKind{ + + NUMERIC, + BOOLEAN, + VARIABLE, + FUNCTION, + CLAUSE, + CLAUSE_CLOSE, + }, + }, + + lexerState{ + + kind: TERNARY, + isEOF: false, + isNullable: false, + validNextKinds: []TokenKind{ + + PREFIX, + NUMERIC, + BOOLEAN, + STRING, + TIME, + VARIABLE, + FUNCTION, + CLAUSE, + SEPARATOR, + }, + }, + lexerState{ + + kind: FUNCTION, + isEOF: false, + isNullable: false, + validNextKinds: []TokenKind{ + CLAUSE, + }, + }, + lexerState{ + + kind: SEPARATOR, + isEOF: false, + isNullable: true, + validNextKinds: []TokenKind{ + + PREFIX, + NUMERIC, + BOOLEAN, + STRING, + TIME, + VARIABLE, + FUNCTION, + CLAUSE, + }, + }, +} + +func (this lexerState) canTransitionTo(kind TokenKind) bool { + + for _, validKind := range this.validNextKinds { + + if validKind == kind { + return true + } + } + + return false +} + +func checkExpressionSyntax(tokens []ExpressionToken) error { + + var state lexerState + var lastToken ExpressionToken + var err error + + state = validLexerStates[0] + + for _, token := range tokens { + + if !state.canTransitionTo(token.Kind) { + + // call out a specific error for tokens looking like they want to be functions. + if lastToken.Kind == VARIABLE && token.Kind == CLAUSE { + return errors.New("Undefined function " + lastToken.Value.(string)) + } + + firstStateName := fmt.Sprintf("%s [%v]", state.kind.String(), lastToken.Value) + nextStateName := fmt.Sprintf("%s [%v]", token.Kind.String(), token.Value) + + return errors.New("Cannot transition token types from " + firstStateName + " to " + nextStateName) + } + + state, err = getLexerStateForToken(token.Kind) + if err != nil { + return err + } + + if !state.isNullable && token.Value == nil { + + errorMsg := fmt.Sprintf("Token kind '%v' cannot have a nil value", token.Kind.String()) + return errors.New(errorMsg) + } + + lastToken = token + } + + if !state.isEOF { + return errors.New("Unexpected end of expression") + } + return nil +} + +func getLexerStateForToken(kind TokenKind) (lexerState, error) { + + for _, possibleState := range validLexerStates { + + if possibleState.kind == kind { + return possibleState, nil + } + } + + errorMsg := fmt.Sprintf("No lexer state found for token kind '%v'\n", kind.String()) + return validLexerStates[0], errors.New(errorMsg) +} diff --git a/vendor/github.com/Knetic/govaluate/lexerStream.go b/vendor/github.com/Knetic/govaluate/lexerStream.go new file mode 100644 index 000000000..b72e6bdb1 --- /dev/null +++ b/vendor/github.com/Knetic/govaluate/lexerStream.go @@ -0,0 +1,39 @@ +package govaluate + +type lexerStream struct { + source []rune + position int + length int +} + +func newLexerStream(source string) *lexerStream { + + var ret *lexerStream + var runes []rune + + for _, character := range source { + runes = append(runes, character) + } + + ret = new(lexerStream) + ret.source = runes + ret.length = len(runes) + return ret +} + +func (this *lexerStream) readCharacter() rune { + + var character rune + + character = this.source[this.position] + this.position += 1 + return character +} + +func (this *lexerStream) rewind(amount int) { + this.position -= amount +} + +func (this lexerStream) canRead() bool { + return this.position < this.length +} diff --git a/vendor/github.com/Knetic/govaluate/parameters.go b/vendor/github.com/Knetic/govaluate/parameters.go new file mode 100644 index 000000000..6c5b9ecb5 --- /dev/null +++ b/vendor/github.com/Knetic/govaluate/parameters.go @@ -0,0 +1,32 @@ +package govaluate + +import ( + "errors" +) + +/* + Parameters is a collection of named parameters that can be used by an EvaluableExpression to retrieve parameters + when an expression tries to use them. +*/ +type Parameters interface { + + /* + Get gets the parameter of the given name, or an error if the parameter is unavailable. + Failure to find the given parameter should be indicated by returning an error. + */ + Get(name string) (interface{}, error) +} + +type MapParameters map[string]interface{} + +func (p MapParameters) Get(name string) (interface{}, error) { + + value, found := p[name] + + if !found { + errorMessage := "No parameter '" + name + "' found." + return nil, errors.New(errorMessage) + } + + return value, nil +} diff --git a/vendor/github.com/Knetic/govaluate/parsing.go b/vendor/github.com/Knetic/govaluate/parsing.go new file mode 100644 index 000000000..7eae71152 --- /dev/null +++ b/vendor/github.com/Knetic/govaluate/parsing.go @@ -0,0 +1,450 @@ +package govaluate + +import ( + "bytes" + "errors" + "fmt" + "regexp" + "strconv" + "time" + "unicode" +) + +func parseTokens(expression string, functions map[string]ExpressionFunction) ([]ExpressionToken, error) { + + var ret []ExpressionToken + var token ExpressionToken + var stream *lexerStream + var state lexerState + var err error + var found bool + + stream = newLexerStream(expression) + state = validLexerStates[0] + + for stream.canRead() { + + token, err, found = readToken(stream, state, functions) + + if err != nil { + return ret, err + } + + if !found { + break + } + + state, err = getLexerStateForToken(token.Kind) + if err != nil { + return ret, err + } + + // append this valid token + ret = append(ret, token) + } + + err = checkBalance(ret) + if err != nil { + return nil, err + } + + return ret, nil +} + +func readToken(stream *lexerStream, state lexerState, functions map[string]ExpressionFunction) (ExpressionToken, error, bool) { + + var function ExpressionFunction + var ret ExpressionToken + var tokenValue interface{} + var tokenTime time.Time + var tokenString string + var kind TokenKind + var character rune + var found bool + var completed bool + var err error + + // numeric is 0-9, or . + // string starts with ' + // variable is alphanumeric, always starts with a letter + // bracket always means variable + // symbols are anything non-alphanumeric + // all others read into a buffer until they reach the end of the stream + for stream.canRead() { + + character = stream.readCharacter() + + if unicode.IsSpace(character) { + continue + } + + kind = UNKNOWN + + // numeric constant + if isNumeric(character) { + + tokenString = readTokenUntilFalse(stream, isNumeric) + tokenValue, err = strconv.ParseFloat(tokenString, 64) + + if err != nil { + errorMsg := fmt.Sprintf("Unable to parse numeric value '%v' to float64\n", tokenString) + return ExpressionToken{}, errors.New(errorMsg), false + } + kind = NUMERIC + break + } + + // comma, separator + if character == ',' { + + tokenValue = "," + kind = SEPARATOR + break + } + + // escaped variable + if character == '[' { + + tokenValue, completed = readUntilFalse(stream, true, false, true, isNotClosingBracket) + kind = VARIABLE + + if !completed { + return ExpressionToken{}, errors.New("Unclosed parameter bracket"), false + } + + // above method normally rewinds us to the closing bracket, which we want to skip. + stream.rewind(-1) + break + } + + // regular variable - or function? + if unicode.IsLetter(character) { + + tokenString = readTokenUntilFalse(stream, isVariableName) + + tokenValue = tokenString + kind = VARIABLE + + // boolean? + if tokenValue == "true" { + + kind = BOOLEAN + tokenValue = true + } else { + + if tokenValue == "false" { + + kind = BOOLEAN + tokenValue = false + } + } + + // textual operator? + if tokenValue == "in" || tokenValue == "IN" { + + // force lower case for consistency + tokenValue = "in" + kind = COMPARATOR + } + + // function? + function, found = functions[tokenString] + if found { + kind = FUNCTION + tokenValue = function + } + break + } + + if !isNotQuote(character) { + tokenValue, completed = readUntilFalse(stream, true, false, true, isNotQuote) + + if !completed { + return ExpressionToken{}, errors.New("Unclosed string literal"), false + } + + // advance the stream one position, since reading until false assumes the terminator is a real token + stream.rewind(-1) + + // check to see if this can be parsed as a time. + tokenTime, found = tryParseTime(tokenValue.(string)) + if found { + kind = TIME + tokenValue = tokenTime + } else { + kind = STRING + } + break + } + + if character == '(' { + tokenValue = character + kind = CLAUSE + break + } + + if character == ')' { + tokenValue = character + kind = CLAUSE_CLOSE + break + } + + // must be a known symbol + tokenString = readTokenUntilFalse(stream, isNotAlphanumeric) + tokenValue = tokenString + + // quick hack for the case where "-" can mean "prefixed negation" or "minus", which are used + // very differently. + if state.canTransitionTo(PREFIX) { + _, found = prefixSymbols[tokenString] + if found { + + kind = PREFIX + break + } + } + _, found = modifierSymbols[tokenString] + if found { + + kind = MODIFIER + break + } + + _, found = logicalSymbols[tokenString] + if found { + + kind = LOGICALOP + break + } + + _, found = comparatorSymbols[tokenString] + if found { + + kind = COMPARATOR + break + } + + _, found = ternarySymbols[tokenString] + if found { + + kind = TERNARY + break + } + + errorMessage := fmt.Sprintf("Invalid token: '%s'", tokenString) + return ret, errors.New(errorMessage), false + } + + ret.Kind = kind + ret.Value = tokenValue + + return ret, nil, (kind != UNKNOWN) +} + +func readTokenUntilFalse(stream *lexerStream, condition func(rune) bool) string { + + var ret string + + stream.rewind(1) + ret, _ = readUntilFalse(stream, false, true, true, condition) + return ret +} + +/* + Returns the string that was read until the given [condition] was false, or whitespace was broken. + Returns false if the stream ended before whitespace was broken or condition was met. +*/ +func readUntilFalse(stream *lexerStream, includeWhitespace bool, breakWhitespace bool, allowEscaping bool, condition func(rune) bool) (string, bool) { + + var tokenBuffer bytes.Buffer + var character rune + var conditioned bool + + conditioned = false + + for stream.canRead() { + + character = stream.readCharacter() + + // Use backslashes to escape anything + if allowEscaping && character == '\\' { + + character = stream.readCharacter() + tokenBuffer.WriteString(string(character)) + continue + } + + if unicode.IsSpace(character) { + + if breakWhitespace && tokenBuffer.Len() > 0 { + conditioned = true + break + } + if !includeWhitespace { + continue + } + } + + if condition(character) { + tokenBuffer.WriteString(string(character)) + } else { + conditioned = true + stream.rewind(1) + break + } + } + + return tokenBuffer.String(), conditioned +} + +/* + Checks to see if any optimizations can be performed on the given [tokens], which form a complete, valid expression. + The returns slice will represent the optimized (or unmodified) list of tokens to use. +*/ +func optimizeTokens(tokens []ExpressionToken) ([]ExpressionToken, error) { + + var token ExpressionToken + var symbol OperatorSymbol + var err error + var index int + + for index, token = range tokens { + + // if we find a regex operator, and the right-hand value is a constant, precompile and replace with a pattern. + if token.Kind != COMPARATOR { + continue + } + + symbol = comparatorSymbols[token.Value.(string)] + if symbol != REQ && symbol != NREQ { + continue + } + + index++ + token = tokens[index] + if token.Kind == STRING { + + token.Kind = PATTERN + token.Value, err = regexp.Compile(token.Value.(string)) + + if err != nil { + return tokens, err + } + + tokens[index] = token + } + } + return tokens, nil +} + +/* + Checks the balance of tokens which have multiple parts, such as parenthesis. +*/ +func checkBalance(tokens []ExpressionToken) error { + + var stream *tokenStream + var token ExpressionToken + var parens int + + stream = newTokenStream(tokens) + + for stream.hasNext() { + + token = stream.next() + if token.Kind == CLAUSE { + parens++ + continue + } + if token.Kind == CLAUSE_CLOSE { + parens-- + continue + } + } + + if parens != 0 { + return errors.New("Unbalanced parenthesis") + } + return nil +} + +func isNumeric(character rune) bool { + + return unicode.IsDigit(character) || character == '.' +} + +func isNotQuote(character rune) bool { + + return character != '\'' && character != '"' +} + +func isNotAlphanumeric(character rune) bool { + + return !(unicode.IsDigit(character) || + unicode.IsLetter(character) || + character == '(' || + character == ')' || + !isNotQuote(character)) +} + +func isVariableName(character rune) bool { + + return unicode.IsLetter(character) || + unicode.IsDigit(character) || + character == '_' +} + +func isNotClosingBracket(character rune) bool { + + return character != ']' +} + +/* + Attempts to parse the [candidate] as a Time. + Tries a series of standardized date formats, returns the Time if one applies, + otherwise returns false through the second return. +*/ +func tryParseTime(candidate string) (time.Time, bool) { + + var ret time.Time + var found bool + + timeFormats := [...]string{ + time.ANSIC, + time.UnixDate, + time.RubyDate, + time.Kitchen, + time.RFC3339, + time.RFC3339Nano, + "2006-01-02", // RFC 3339 + "2006-01-02 15:04", // RFC 3339 with minutes + "2006-01-02 15:04:05", // RFC 3339 with seconds + "2006-01-02 15:04:05-07:00", // RFC 3339 with seconds and timezone + "2006-01-02T15Z0700", // ISO8601 with hour + "2006-01-02T15:04Z0700", // ISO8601 with minutes + "2006-01-02T15:04:05Z0700", // ISO8601 with seconds + "2006-01-02T15:04:05.999999999Z0700", // ISO8601 with nanoseconds + } + + for _, format := range timeFormats { + + ret, found = tryParseExactTime(candidate, format) + if found { + return ret, true + } + } + + return time.Now(), false +} + +func tryParseExactTime(candidate string, format string) (time.Time, bool) { + + var ret time.Time + var err error + + ret, err = time.ParseInLocation(format, candidate, time.Local) + if err != nil { + return time.Now(), false + } + + return ret, true +} diff --git a/vendor/github.com/Knetic/govaluate/sanitizedParameters.go b/vendor/github.com/Knetic/govaluate/sanitizedParameters.go new file mode 100644 index 000000000..5cfab9b08 --- /dev/null +++ b/vendor/github.com/Knetic/govaluate/sanitizedParameters.go @@ -0,0 +1,71 @@ +package govaluate + +// sanitizedParameters is a wrapper for Parameters that does sanitization as +// parameters are accessed. +type sanitizedParameters struct { + orig Parameters +} + +func (p sanitizedParameters) Get(key string) (interface{}, error) { + value, err := p.orig.Get(key) + if err != nil { + return nil, err + } + + // should be converted to fixed point? + if isFixedPoint(value) { + return castFixedPoint(value), nil + } + + return value, nil +} + +func isFixedPoint(value interface{}) bool { + + switch value.(type) { + case uint8: + return true + case uint16: + return true + case uint32: + return true + case uint64: + return true + case int8: + return true + case int16: + return true + case int32: + return true + case int64: + return true + case int: + return true + } + return false +} + +func castFixedPoint(value interface{}) float64 { + switch value.(type) { + case uint8: + return float64(value.(uint8)) + case uint16: + return float64(value.(uint16)) + case uint32: + return float64(value.(uint32)) + case uint64: + return float64(value.(uint64)) + case int8: + return float64(value.(int8)) + case int16: + return float64(value.(int16)) + case int32: + return float64(value.(int32)) + case int64: + return float64(value.(int64)) + case int: + return float64(value.(int)) + } + + return 0.0 +} diff --git a/vendor/github.com/Knetic/govaluate/stagePlanner.go b/vendor/github.com/Knetic/govaluate/stagePlanner.go new file mode 100644 index 000000000..a195c7006 --- /dev/null +++ b/vendor/github.com/Knetic/govaluate/stagePlanner.go @@ -0,0 +1,675 @@ +package govaluate + +import ( + "errors" + "time" + "fmt" +) + +var stageSymbolMap = map[OperatorSymbol]evaluationOperator{ + EQ: equalStage, + NEQ: notEqualStage, + GT: gtStage, + LT: ltStage, + GTE: gteStage, + LTE: lteStage, + REQ: regexStage, + NREQ: notRegexStage, + AND: andStage, + OR: orStage, + IN: inStage, + BITWISE_OR: bitwiseOrStage, + BITWISE_AND: bitwiseAndStage, + BITWISE_XOR: bitwiseXORStage, + BITWISE_LSHIFT: leftShiftStage, + BITWISE_RSHIFT: rightShiftStage, + PLUS: addStage, + MINUS: subtractStage, + MULTIPLY: multiplyStage, + DIVIDE: divideStage, + MODULUS: modulusStage, + EXPONENT: exponentStage, + NEGATE: negateStage, + INVERT: invertStage, + BITWISE_NOT: bitwiseNotStage, + TERNARY_TRUE: ternaryIfStage, + TERNARY_FALSE: ternaryElseStage, + COALESCE: ternaryElseStage, + SEPARATE: separatorStage, +} + +/* + A "precedent" is a function which will recursively parse new evaluateionStages from a given stream of tokens. + It's called a `precedent` because it is expected to handle exactly what precedence of operator, + and defer to other `precedent`s for other operators. +*/ +type precedent func(stream *tokenStream) (*evaluationStage, error) + +/* + A convenience function for specifying the behavior of a `precedent`. + Most `precedent` functions can be described by the same function, just with different type checks, symbols, and error formats. + This struct is passed to `makePrecedentFromPlanner` to create a `precedent` function. +*/ +type precedencePlanner struct { + validSymbols map[string]OperatorSymbol + validKinds []TokenKind + + typeErrorFormat string + + next precedent + nextRight precedent +} + +var planPrefix precedent +var planExponential precedent +var planMultiplicative precedent +var planAdditive precedent +var planBitwise precedent +var planShift precedent +var planComparator precedent +var planLogicalAnd precedent +var planLogicalOr precedent +var planTernary precedent +var planSeparator precedent + +func init() { + + // all these stages can use the same code (in `planPrecedenceLevel`) to execute, + // they simply need different type checks, symbols, and recursive precedents. + // While not all precedent phases are listed here, most can be represented this way. + planPrefix = makePrecedentFromPlanner(&precedencePlanner{ + validSymbols: prefixSymbols, + validKinds: []TokenKind{PREFIX}, + typeErrorFormat: prefixErrorFormat, + nextRight: planFunction, + }) + planExponential = makePrecedentFromPlanner(&precedencePlanner{ + validSymbols: exponentialSymbolsS, + validKinds: []TokenKind{MODIFIER}, + typeErrorFormat: modifierErrorFormat, + next: planFunction, + }) + planMultiplicative = makePrecedentFromPlanner(&precedencePlanner{ + validSymbols: multiplicativeSymbols, + validKinds: []TokenKind{MODIFIER}, + typeErrorFormat: modifierErrorFormat, + next: planExponential, + }) + planAdditive = makePrecedentFromPlanner(&precedencePlanner{ + validSymbols: additiveSymbols, + validKinds: []TokenKind{MODIFIER}, + typeErrorFormat: modifierErrorFormat, + next: planMultiplicative, + }) + planShift = makePrecedentFromPlanner(&precedencePlanner{ + validSymbols: bitwiseShiftSymbols, + validKinds: []TokenKind{MODIFIER}, + typeErrorFormat: modifierErrorFormat, + next: planAdditive, + }) + planBitwise = makePrecedentFromPlanner(&precedencePlanner{ + validSymbols: bitwiseSymbols, + validKinds: []TokenKind{MODIFIER}, + typeErrorFormat: modifierErrorFormat, + next: planShift, + }) + planComparator = makePrecedentFromPlanner(&precedencePlanner{ + validSymbols: comparatorSymbols, + validKinds: []TokenKind{COMPARATOR}, + typeErrorFormat: comparatorErrorFormat, + next: planBitwise, + }) + planLogicalAnd = makePrecedentFromPlanner(&precedencePlanner{ + validSymbols: map[string]OperatorSymbol{"&&": AND}, + validKinds: []TokenKind{LOGICALOP}, + typeErrorFormat: logicalErrorFormat, + next: planComparator, + }) + planLogicalOr = makePrecedentFromPlanner(&precedencePlanner{ + validSymbols: map[string]OperatorSymbol{"||": OR}, + validKinds: []TokenKind{LOGICALOP}, + typeErrorFormat: logicalErrorFormat, + next: planLogicalAnd, + }) + planTernary = makePrecedentFromPlanner(&precedencePlanner{ + validSymbols: ternarySymbols, + validKinds: []TokenKind{TERNARY}, + typeErrorFormat: ternaryErrorFormat, + next: planLogicalOr, + }) + planSeparator = makePrecedentFromPlanner(&precedencePlanner{ + validSymbols: separatorSymbols, + validKinds: []TokenKind{SEPARATOR}, + next: planTernary, + }) +} + +/* + Given a planner, creates a function which will evaluate a specific precedence level of operators, + and link it to other `precedent`s which recurse to parse other precedence levels. +*/ +func makePrecedentFromPlanner(planner *precedencePlanner) precedent { + + var generated precedent + var nextRight precedent + + generated = func(stream *tokenStream) (*evaluationStage, error) { + return planPrecedenceLevel( + stream, + planner.typeErrorFormat, + planner.validSymbols, + planner.validKinds, + nextRight, + planner.next, + ) + } + + if planner.nextRight != nil { + nextRight = planner.nextRight + } else { + nextRight = generated + } + + return generated +} + +/* + Creates a `evaluationStageList` object which represents an execution plan (or tree) + which is used to completely evaluate a set of tokens at evaluation-time. + The three stages of evaluation can be thought of as parsing strings to tokens, then tokens to a stage list, then evaluation with parameters. +*/ +func planStages(tokens []ExpressionToken) (*evaluationStage, error) { + + stream := newTokenStream(tokens) + + stage, err := planTokens(stream) + if err != nil { + return nil, err + } + + // while we're now fully-planned, we now need to re-order same-precedence operators. + // this could probably be avoided with a different planning method + reorderStages(stage) + + stage = elideLiterals(stage) + return stage, nil +} + +func planTokens(stream *tokenStream) (*evaluationStage, error) { + + if !stream.hasNext() { + return nil, nil + } + + return planSeparator(stream) +} + +/* + The most usual method of parsing an evaluation stage for a given precedence. + Most stages use the same logic +*/ +func planPrecedenceLevel( + stream *tokenStream, + typeErrorFormat string, + validSymbols map[string]OperatorSymbol, + validKinds []TokenKind, + rightPrecedent precedent, + leftPrecedent precedent) (*evaluationStage, error) { + + var token ExpressionToken + var symbol OperatorSymbol + var leftStage, rightStage *evaluationStage + var checks typeChecks + var err error + var keyFound bool + + if leftPrecedent != nil { + + leftStage, err = leftPrecedent(stream) + if err != nil { + return nil, err + } + } + + for stream.hasNext() { + + token = stream.next() + + if len(validKinds) > 0 { + + keyFound = false + for _, kind := range validKinds { + if kind == token.Kind { + keyFound = true + break + } + } + + if !keyFound { + break + } + } + + if validSymbols != nil { + + if !isString(token.Value) { + break + } + + symbol, keyFound = validSymbols[token.Value.(string)] + if !keyFound { + break + } + } + + if rightPrecedent != nil { + rightStage, err = rightPrecedent(stream) + if err != nil { + return nil, err + } + } + + checks = findTypeChecks(symbol) + + return &evaluationStage{ + + symbol: symbol, + leftStage: leftStage, + rightStage: rightStage, + operator: stageSymbolMap[symbol], + + leftTypeCheck: checks.left, + rightTypeCheck: checks.right, + typeCheck: checks.combined, + typeErrorFormat: typeErrorFormat, + }, nil + } + + stream.rewind() + return leftStage, nil +} + +/* + A special case where functions need to be of higher precedence than values, and need a special wrapped execution stage operator. +*/ +func planFunction(stream *tokenStream) (*evaluationStage, error) { + + var token ExpressionToken + var rightStage *evaluationStage + var err error + + token = stream.next() + + if token.Kind != FUNCTION { + stream.rewind() + return planValue(stream) + } + + rightStage, err = planValue(stream) + if err != nil { + return nil, err + } + + return &evaluationStage{ + + symbol: FUNCTIONAL, + rightStage: rightStage, + operator: makeFunctionStage(token.Value.(ExpressionFunction)), + typeErrorFormat: "Unable to run function '%v': %v", + }, nil +} + +/* + A truly special precedence function, this handles all the "lowest-case" errata of the process, including literals, parmeters, + clauses, and prefixes. +*/ +func planValue(stream *tokenStream) (*evaluationStage, error) { + + var token ExpressionToken + var symbol OperatorSymbol + var ret *evaluationStage + var operator evaluationOperator + var err error + + token = stream.next() + + switch token.Kind { + + case CLAUSE: + + ret, err = planTokens(stream) + if err != nil { + return nil, err + } + + // advance past the CLAUSE_CLOSE token. We know that it's a CLAUSE_CLOSE, because at parse-time we check for unbalanced parens. + stream.next() + + // the stage we got represents all of the logic contained within the parens + // but for technical reasons, we need to wrap this stage in a "noop" stage which breaks long chains of precedence. + // see github #33. + ret = &evaluationStage { + rightStage: ret, + operator: noopStageRight, + symbol: NOOP, + } + + return ret, nil + + case CLAUSE_CLOSE: + + // when functions have empty params, this will be hit. In this case, we don't have any evaluation stage to do, + // so we just return nil so that the stage planner continues on its way. + stream.rewind() + return nil, nil + + case VARIABLE: + operator = makeParameterStage(token.Value.(string)) + + case NUMERIC: + fallthrough + case STRING: + fallthrough + case PATTERN: + fallthrough + case BOOLEAN: + symbol = LITERAL + operator = makeLiteralStage(token.Value) + case TIME: + symbol = LITERAL + operator = makeLiteralStage(float64(token.Value.(time.Time).Unix())) + + case PREFIX: + stream.rewind() + return planPrefix(stream) + } + + if operator == nil { + errorMsg := fmt.Sprintf("Unable to plan token kind: '%s', value: '%v'", token.Kind.String(), token.Value) + return nil, errors.New(errorMsg) + } + + return &evaluationStage{ + symbol: symbol, + operator: operator, + }, nil +} + +/* + Convenience function to pass a triplet of typechecks between `findTypeChecks` and `planPrecedenceLevel`. + Each of these members may be nil, which indicates that type does not matter for that value. +*/ +type typeChecks struct { + left stageTypeCheck + right stageTypeCheck + combined stageCombinedTypeCheck +} + +/* + Maps a given [symbol] to a set of typechecks to be used during runtime. +*/ +func findTypeChecks(symbol OperatorSymbol) typeChecks { + + switch symbol { + case GT: + fallthrough + case LT: + fallthrough + case GTE: + fallthrough + case LTE: + return typeChecks{ + combined: comparatorTypeCheck, + } + case REQ: + fallthrough + case NREQ: + return typeChecks{ + left: isString, + right: isRegexOrString, + } + case AND: + fallthrough + case OR: + return typeChecks{ + left: isBool, + right: isBool, + } + case IN: + return typeChecks{ + right: isArray, + } + case BITWISE_LSHIFT: + fallthrough + case BITWISE_RSHIFT: + fallthrough + case BITWISE_OR: + fallthrough + case BITWISE_AND: + fallthrough + case BITWISE_XOR: + return typeChecks{ + left: isFloat64, + right: isFloat64, + } + case PLUS: + return typeChecks{ + combined: additionTypeCheck, + } + case MINUS: + fallthrough + case MULTIPLY: + fallthrough + case DIVIDE: + fallthrough + case MODULUS: + fallthrough + case EXPONENT: + return typeChecks{ + left: isFloat64, + right: isFloat64, + } + case NEGATE: + return typeChecks{ + right: isFloat64, + } + case INVERT: + return typeChecks{ + right: isBool, + } + case BITWISE_NOT: + return typeChecks{ + right: isFloat64, + } + case TERNARY_TRUE: + return typeChecks{ + left: isBool, + } + + // unchecked cases + case EQ: + fallthrough + case NEQ: + return typeChecks{} + case TERNARY_FALSE: + fallthrough + case COALESCE: + fallthrough + default: + return typeChecks{} + } +} + +/* + During stage planning, stages of equal precedence are parsed such that they'll be evaluated in reverse order. + For commutative operators like "+" or "-", it's no big deal. But for order-specific operators, it ruins the expected result. +*/ +func reorderStages(rootStage *evaluationStage) { + + // traverse every rightStage until we find multiples in a row of the same precedence. + var identicalPrecedences []*evaluationStage + var currentStage, nextStage *evaluationStage + var precedence, currentPrecedence operatorPrecedence + + nextStage = rootStage + precedence = findOperatorPrecedenceForSymbol(rootStage.symbol) + + for nextStage != nil { + + currentStage = nextStage + nextStage = currentStage.rightStage + + // left depth first, since this entire method only looks for precedences down the right side of the tree + if currentStage.leftStage != nil { + reorderStages(currentStage.leftStage) + } + + currentPrecedence = findOperatorPrecedenceForSymbol(currentStage.symbol) + + if currentPrecedence == precedence { + identicalPrecedences = append(identicalPrecedences, currentStage) + continue + } + + // precedence break. + // See how many in a row we had, and reorder if there's more than one. + if len(identicalPrecedences) > 1 { + mirrorStageSubtree(identicalPrecedences) + } + + identicalPrecedences = []*evaluationStage{currentStage} + precedence = currentPrecedence + } + + if len(identicalPrecedences) > 1 { + mirrorStageSubtree(identicalPrecedences) + } +} + +/* + Performs a "mirror" on a subtree of stages. + This mirror functionally inverts the order of execution for all members of the [stages] list. + That list is assumed to be a root-to-leaf (ordered) list of evaluation stages, where each is a right-hand stage of the last. +*/ +func mirrorStageSubtree(stages []*evaluationStage) { + + var rootStage, inverseStage, carryStage, frontStage *evaluationStage + + stagesLength := len(stages) + + // reverse all right/left + for _, frontStage = range stages { + + carryStage = frontStage.rightStage + frontStage.rightStage = frontStage.leftStage + frontStage.leftStage = carryStage + } + + // end left swaps with root right + rootStage = stages[0] + frontStage = stages[stagesLength-1] + + carryStage = frontStage.leftStage + frontStage.leftStage = rootStage.rightStage + rootStage.rightStage = carryStage + + // for all non-root non-end stages, right is swapped with inverse stage right in list + for i := 0; i < (stagesLength-2)/2+1; i++ { + + frontStage = stages[i+1] + inverseStage = stages[stagesLength-i-1] + + carryStage = frontStage.rightStage + frontStage.rightStage = inverseStage.rightStage + inverseStage.rightStage = carryStage + } + + // swap all other information with inverse stages + for i := 0; i < stagesLength/2; i++ { + + frontStage = stages[i] + inverseStage = stages[stagesLength-i-1] + frontStage.swapWith(inverseStage) + } +} + +/* + Recurses through all operators in the entire tree, eliding operators where both sides are literals. +*/ +func elideLiterals(root *evaluationStage) *evaluationStage { + + if root.leftStage != nil { + root.leftStage = elideLiterals(root.leftStage) + } + + if root.rightStage != nil { + root.rightStage = elideLiterals(root.rightStage) + } + + return elideStage(root) +} + +/* + Elides a specific stage, if possible. + Returns the unmodified [root] stage if it cannot or should not be elided. + Otherwise, returns a new stage representing the condensed value from the elided stages. +*/ +func elideStage(root *evaluationStage) *evaluationStage { + + var leftValue, rightValue, result interface{} + var err error + + // right side must be a non-nil value. Left side must be nil or a value. + if root.rightStage == nil || + root.rightStage.symbol != LITERAL || + root.leftStage == nil || + root.leftStage.symbol != LITERAL { + return root + } + + // don't elide some operators + switch root.symbol { + case SEPARATE: + fallthrough + case IN: + return root + } + + // both sides are values, get their actual values. + // errors should be near-impossible here. If we encounter them, just abort this optimization. + leftValue, err = root.leftStage.operator(nil, nil, nil) + if err != nil { + return root + } + + rightValue, err = root.rightStage.operator(nil, nil, nil) + if err != nil { + return root + } + + // typcheck, since the grammar checker is a bit loose with which operator symbols go together. + err = typeCheck(root.leftTypeCheck, leftValue, root.symbol, root.typeErrorFormat) + if err != nil { + return root + } + + err = typeCheck(root.rightTypeCheck, rightValue, root.symbol, root.typeErrorFormat) + if err != nil { + return root + } + + if root.typeCheck != nil && !root.typeCheck(leftValue, rightValue) { + return root + } + + // pre-calculate, and return a new stage representing the result. + result, err = root.operator(leftValue, rightValue, nil) + if err != nil { + return root + } + + return &evaluationStage { + symbol: LITERAL, + operator: makeLiteralStage(result), + } +} diff --git a/vendor/github.com/Knetic/govaluate/test.sh b/vendor/github.com/Knetic/govaluate/test.sh new file mode 100755 index 000000000..6a67ae7aa --- /dev/null +++ b/vendor/github.com/Knetic/govaluate/test.sh @@ -0,0 +1,32 @@ +#!/bin/bash + +# Script that runs tests, code coverage, and benchmarks all at once. +# Builds a symlink in /tmp, mostly to avoid messing with GOPATH at the user's shell level. + +TEMPORARY_PATH="/tmp/govaluate_test" +SRC_PATH="${TEMPORARY_PATH}/src" +FULL_PATH="${TEMPORARY_PATH}/src/govaluate" + +# set up temporary directory +rm -rf "${FULL_PATH}" +mkdir -p "${SRC_PATH}" + +ln -s $(pwd) "${FULL_PATH}" +export GOPATH="${TEMPORARY_PATH}" + +pushd "${TEMPORARY_PATH}/src/govaluate" + +# run the actual tests. +export GOVALUATE_TORTURE_TEST="true" +go test -bench=. -benchmem -coverprofile coverage.out +status=$? + +if [ "${status}" != 0 ]; +then + exit $status +fi + +# coverage +go tool cover -func=coverage.out + +popd diff --git a/vendor/github.com/Knetic/govaluate/tokenStream.go b/vendor/github.com/Knetic/govaluate/tokenStream.go new file mode 100644 index 000000000..d0029209d --- /dev/null +++ b/vendor/github.com/Knetic/govaluate/tokenStream.go @@ -0,0 +1,36 @@ +package govaluate + +type tokenStream struct { + tokens []ExpressionToken + index int + tokenLength int +} + +func newTokenStream(tokens []ExpressionToken) *tokenStream { + + var ret *tokenStream + + ret = new(tokenStream) + ret.tokens = tokens + ret.tokenLength = len(tokens) + return ret +} + +func (this *tokenStream) rewind() { + this.index -= 1 +} + +func (this *tokenStream) next() ExpressionToken { + + var token ExpressionToken + + token = this.tokens[this.index] + + this.index += 1 + return token +} + +func (this tokenStream) hasNext() bool { + + return this.index < this.tokenLength +} diff --git a/vendor/github.com/casbin/casbin/LICENSE b/vendor/github.com/casbin/casbin/LICENSE new file mode 100644 index 000000000..8dada3eda --- /dev/null +++ b/vendor/github.com/casbin/casbin/LICENSE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "{}" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright {yyyy} {name of copyright owner} + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/vendor/github.com/casbin/casbin/README.md b/vendor/github.com/casbin/casbin/README.md new file mode 100644 index 000000000..185afe65f --- /dev/null +++ b/vendor/github.com/casbin/casbin/README.md @@ -0,0 +1,372 @@ +Casbin +==== + +[![Go Report Card](https://goreportcard.com/badge/github.com/casbin/casbin)](https://goreportcard.com/report/github.com/casbin/casbin) +[![Build Status](https://travis-ci.org/casbin/casbin.svg?branch=master)](https://travis-ci.org/casbin/casbin) +[![Coverage Status](https://coveralls.io/repos/github/casbin/casbin/badge.svg?branch=master)](https://coveralls.io/github/casbin/casbin?branch=master) +[![Godoc](https://godoc.org/github.com/casbin/casbin?status.svg)](https://godoc.org/github.com/casbin/casbin) +[![Release](https://img.shields.io/github/release/casbin/casbin.svg)](https://github.com/casbin/casbin/releases/latest) +[![Gitter](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/casbin/lobby) +[![Patreon](https://img.shields.io/badge/patreon-donate-yellow.svg)](http://www.patreon.com/yangluo) +[![Sourcegraph Badge](https://sourcegraph.com/github.com/casbin/casbin/-/badge.svg)](https://sourcegraph.com/github.com/casbin/casbin?badge) + +**News**: still worry about how to write the correct Casbin policy? ``Casbin online editor`` is coming to help! Try it at: http://casbin.org/editor/ + +![casbin Logo](casbin-logo.png) + +Casbin is a powerful and efficient open-source access control library for Golang projects. It provides support for enforcing authorization based on various [access control models](https://en.wikipedia.org/wiki/Computer_security_model). + +## All the languages supported by Casbin: + +[![golang](https://casbin.org/docs/assets/langs/golang.png)](https://github.com/casbin/casbin) | [![java](https://casbin.org/docs/assets/langs/java.png)](https://github.com/casbin/jcasbin) | [![nodejs](https://casbin.org/docs/assets/langs/nodejs.png)](https://github.com/casbin/node-casbin) | [![php](https://casbin.org/docs/assets/langs/php.png)](https://github.com/php-casbin/php-casbin) +----|----|----|---- +[Casbin](https://github.com/casbin/casbin) | [jCasbin](https://github.com/casbin/jcasbin) | [node-Casbin](https://github.com/casbin/node-casbin) | [PHP-Casbin](https://github.com/php-casbin/php-casbin) +production-ready | production-ready | production-ready | production-ready + +## Table of contents + +- [Supported models](#supported-models) +- [How it works?](#how-it-works) +- [Features](#features) +- [Installation](#installation) +- [Documentation](#documentation) +- [Online editor](#online-editor) +- [Tutorials](#tutorials) +- [Get started](#get-started) +- [Policy management](#policy-management) +- [Policy persistence](#policy-persistence) +- [Policy consistence between multiple nodes](#policy-consistence-between-multiple-nodes) +- [Role manager](#role-manager) +- [Multi-threading](#multi-threading) +- [Benchmarks](#benchmarks) +- [Examples](#examples) +- [How to use Casbin as a service?](#how-to-use-casbin-as-a-service) +- [Our adopters](#our-adopters) + +## Supported models + +1. [**ACL (Access Control List)**](https://en.wikipedia.org/wiki/Access_control_list) +2. **ACL with [superuser](https://en.wikipedia.org/wiki/Superuser)** +3. **ACL without users**: especially useful for systems that don't have authentication or user log-ins. +3. **ACL without resources**: some scenarios may target for a type of resources instead of an individual resource by using permissions like ``write-article``, ``read-log``. It doesn't control the access to a specific article or log. +4. **[RBAC (Role-Based Access Control)](https://en.wikipedia.org/wiki/Role-based_access_control)** +5. **RBAC with resource roles**: both users and resources can have roles (or groups) at the same time. +6. **RBAC with domains/tenants**: users can have different role sets for different domains/tenants. +7. **[ABAC (Attribute-Based Access Control)](https://en.wikipedia.org/wiki/Attribute-Based_Access_Control)**: syntax sugar like ``resource.Owner`` can be used to get the attribute for a resource. +8. **[RESTful](https://en.wikipedia.org/wiki/Representational_state_transfer)**: supports paths like ``/res/*``, ``/res/:id`` and HTTP methods like ``GET``, ``POST``, ``PUT``, ``DELETE``. +9. **Deny-override**: both allow and deny authorizations are supported, deny overrides the allow. +10. **Priority**: the policy rules can be prioritized like firewall rules. + +## How it works? + +In Casbin, an access control model is abstracted into a CONF file based on the **PERM metamodel (Policy, Effect, Request, Matchers)**. So switching or upgrading the authorization mechanism for a project is just as simple as modifying a configuration. You can customize your own access control model by combining the available models. For example, you can get RBAC roles and ABAC attributes together inside one model and share one set of policy rules. + +The most basic and simplest model in Casbin is ACL. ACL's model CONF is: + +```ini +# Request definition +[request_definition] +r = sub, obj, act + +# Policy definition +[policy_definition] +p = sub, obj, act + +# Policy effect +[policy_effect] +e = some(where (p.eft == allow)) + +# Matchers +[matchers] +m = r.sub == p.sub && r.obj == p.obj && r.act == p.act + +``` + +An example policy for ACL model is like: + +``` +p, alice, data1, read +p, bob, data2, write +``` + +It means: + +- alice can read data1 +- bob can write data2 + +We also support multi-line mode by appending '\\' in the end: + +```ini +# Matchers +[matchers] +m = r.sub == p.sub && r.obj == p.obj \ + && r.act == p.act +``` + +Further more, if you are using ABAC, you can try operator `in` like following in Casbin **golang** edition (jCasbin and Node-Casbin are not supported yet): + +```ini +# Matchers +[matchers] +m = r.obj == p.obj && r.act == p.act || r.obj in ('data2', 'data3') +``` + +But you **SHOULD** make sure that the length of the array is **MORE** than **1**, otherwise there will cause it to panic. + +For more operators, you may take a look at [govaluate](https://github.com/Knetic/govaluate) + +## Features + +What Casbin does: + +1. enforce the policy in the classic ``{subject, object, action}`` form or a customized form as you defined, both allow and deny authorizations are supported. +2. handle the storage of the access control model and its policy. +3. manage the role-user mappings and role-role mappings (aka role hierarchy in RBAC). +4. support built-in superuser like ``root`` or ``administrator``. A superuser can do anything without explict permissions. +5. multiple built-in operators to support the rule matching. For example, ``keyMatch`` can map a resource key ``/foo/bar`` to the pattern ``/foo*``. + +What Casbin does NOT do: + +1. authentication (aka verify ``username`` and ``password`` when a user logs in) +2. manage the list of users or roles. I believe it's more convenient for the project itself to manage these entities. Users usually have their passwords, and Casbin is not designed as a password container. However, Casbin stores the user-role mapping for the RBAC scenario. + +## Installation + +``` +go get github.com/casbin/casbin +``` + +## Documentation + +https://casbin.org/docs/en/overview + +## Online editor + +You can also use the online editor (http://casbin.org/editor/) to write your Casbin model and policy in your web browser. It provides functionality such as ``syntax highlighting`` and ``code completion``, just like an IDE for a programming language. + +## Tutorials + +https://casbin.org/docs/en/tutorials + +## Get started + +1. New a Casbin enforcer with a model file and a policy file: + + ```go + e := casbin.NewEnforcer("path/to/model.conf", "path/to/policy.csv") + ``` + +Note: you can also initialize an enforcer with policy in DB instead of file, see [Persistence](#persistence) section for details. + +2. Add an enforcement hook into your code right before the access happens: + + ```go + sub := "alice" // the user that wants to access a resource. + obj := "data1" // the resource that is going to be accessed. + act := "read" // the operation that the user performs on the resource. + + if e.Enforce(sub, obj, act) == true { + // permit alice to read data1 + } else { + // deny the request, show an error + } + ``` + +3. Besides the static policy file, Casbin also provides API for permission management at run-time. For example, You can get all the roles assigned to a user as below: + + ```go + roles := e.GetRoles("alice") + ``` + +See [Policy management APIs](#policy-management) for more usage. + +4. Please refer to the ``_test.go`` files for more usage. + +## Policy management + +Casbin provides two sets of APIs to manage permissions: + +- [Management API](https://github.com/casbin/casbin/blob/master/management_api.go): the primitive API that provides full support for Casbin policy management. See [here](https://github.com/casbin/casbin/blob/master/management_api_test.go) for examples. +- [RBAC API](https://github.com/casbin/casbin/blob/master/rbac_api.go): a more friendly API for RBAC. This API is a subset of Management API. The RBAC users could use this API to simplify the code. See [here](https://github.com/casbin/casbin/blob/master/rbac_api_test.go) for examples. + +We also provide a web-based UI for model management and policy management: + +![model editor](https://hsluoyz.github.io/casbin/ui_model_editor.png) + +![policy editor](https://hsluoyz.github.io/casbin/ui_policy_editor.png) + +## Policy persistence + +In Casbin, the policy storage is implemented as an adapter (aka middleware for Casbin). To keep light-weight, we don't put adapter code in the main library (except the default file adapter). A complete list of Casbin adapters is provided as below. Any 3rd-party contribution on a new adapter is welcomed, please inform us and I will put it in this list:) + +Adapter | Type | Author | Description +----|------|----|---- +[File Adapter (built-in)](https://casbin.org/docs/en/policy-storage#file-adapter-built-in) | File | Casbin | Persistence for [.CSV (Comma-Separated Values)](https://en.wikipedia.org/wiki/Comma-separated_values) files +[Filtered File Adapter (built-in)](https://github.com/casbin/casbin#policy-enforcement-at-scale) | File | [@faceless-saint](https://github.com/faceless-saint) | Persistence for [.CSV (Comma-Separated Values)](https://en.wikipedia.org/wiki/Comma-separated_values) files with policy subset loading support +[Xorm Adapter](https://github.com/casbin/xorm-adapter) | ORM | Casbin | MySQL, PostgreSQL, TiDB, SQLite, SQL Server, Oracle are supported by [Xorm](https://github.com/go-xorm/xorm/) +[Gorm Adapter](https://github.com/casbin/gorm-adapter) | ORM | Casbin | MySQL, PostgreSQL, Sqlite3, SQL Server are supported by [Gorm](https://github.com/jinzhu/gorm/) +[Beego ORM Adapter](https://github.com/casbin/beego-orm-adapter) | ORM | Casbin | MySQL, PostgreSQL, Sqlite3 are supported by [Beego ORM](https://beego.me/docs/mvc/model/overview.md) +[MongoDB Adapter](https://github.com/casbin/mongodb-adapter) | NoSQL | Casbin | Persistence for [MongoDB](https://www.mongodb.com) +[Cassandra Adapter](https://github.com/casbin/cassandra-adapter) | NoSQL | Casbin | Persistence for [Apache Cassandra DB](http://cassandra.apache.org) +[Consul Adapter](https://github.com/ankitm123/consul-adapter) | KV store | [@ankitm123](https://github.com/ankitm123) | Persistence for [HashiCorp Consul](https://www.consul.io/) +[Redis Adapter](https://github.com/casbin/redis-adapter) | KV store | Casbin | Persistence for [Redis](https://redis.io/) +[Protobuf Adapter](https://github.com/casbin/protobuf-adapter) | Stream | Casbin | Persistence for [Google Protocol Buffers](https://developers.google.com/protocol-buffers/) +[JSON Adapter](https://github.com/casbin/json-adapter) | String | Casbin | Persistence for [JSON](https://www.json.org/) +[String Adapter](https://github.com/qiangmzsx/string-adapter) | String | [@qiangmzsx](https://github.com/qiangmzsx) | Persistence for String +[RQLite Adapter](https://github.com/edomosystems/rqlite-adapter) | SQL | [EDOMO Systems](https://github.com/edomosystems) | Persistence for [RQLite](https://github.com/rqlite/rqlite/) +[PostgreSQL Adapter](https://github.com/going/casbin-postgres-adapter) | SQL | [Going](https://github.com/going) | Persistence for [PostgreSQL](https://www.postgresql.org/) +[RethinkDB Adapter](https://github.com/adityapandey9/rethinkdb-adapter) | NoSQL | [@adityapandey9](https://github.com/adityapandey9) | Persistence for [RethinkDB](https://rethinkdb.com/) +[DynamoDB Adapter](https://github.com/HOOQTV/dynacasbin) | NoSQL | [HOOQ](https://github.com/HOOQTV) | Persistence for [Amazon DynamoDB](https://aws.amazon.com/dynamodb/) +[Minio/AWS S3 Adapter](https://github.com/Soluto/casbin-minio-adapter) | Object storage | [Soluto](https://github.com/Soluto) | Persistence for [Minio](https://github.com/minio/minio) and [Amazon S3](https://aws.amazon.com/s3/) +[Bolt Adapter](https://github.com/wirepair/bolt-adapter) | KV store | [@wirepair](https://github.com/wirepair) | Persistence for [Bolt](https://github.com/boltdb/bolt) + +For details of adapters, please refer to the documentation: https://casbin.org/docs/en/policy-storage + +## Policy enforcement at scale + +Some adapters support filtered policy management. This means that the policy loaded by Casbin is a subset of the policy in storage based on a given filter. This allows for efficient policy enforcement in large, multi-tenant environments when parsing the entire policy becomes a performance bottleneck. + +To use filtered policies with a supported adapter, simply call the `LoadFilteredPolicy` method. The valid format for the filter parameter depends on the adapter used. To prevent accidental data loss, the `SavePolicy` method is disabled when a filtered policy is loaded. + +For example, the following code snippet uses the built-in filtered file adapter and the RBAC model with domains. In this case, the filter limits the policy to a single domain. Any policy lines for domains other than `"domain1"` are omitted from the loaded policy: + +```go +import ( + "github.com/casbin/casbin" +) + +enforcer := casbin.NewEnforcer() + +adapter := fileadapter.NewFilteredAdapter("examples/rbac_with_domains_policy.csv") +enforcer.InitWithAdapter("examples/rbac_with_domains_model.conf", adapter) + +filter := &fileadapter.Filter{ + P: []string{"", "domain1"}, + G: []string{"", "", "domain1"}, +} +enforcer.LoadFilteredPolicy(filter) + +// The loaded policy now only contains the entries pertaining to "domain1". +``` + +## Policy consistence between multiple nodes + +We support to use distributed messaging systems like [etcd](https://github.com/coreos/etcd) to keep consistence between multiple Casbin enforcer instances. So our users can concurrently use multiple Casbin enforcers to handle large number of permission checking requests. + +Similar to policy storage adapters, we don't put watcher code in the main library. Any support for a new messaging system should be implemented as a watcher. A complete list of Casbin watchers is provided as below. Any 3rd-party contribution on a new watcher is welcomed, please inform us and I will put it in this list:) + +Watcher | Type | Author | Description +----|------|----|---- +[Etcd Watcher](https://github.com/casbin/etcd-watcher) | KV store | Casbin | Watcher for [etcd](https://github.com/coreos/etcd) +[NATS Watcher](https://github.com/Soluto/casbin-nats-watcher) | Messaging system | [Soluto](https://github.com/Soluto) | Watcher for [NATS](https://nats.io/) +[ZooKeeper Watcher](https://github.com/grepsr/casbin-zk-watcher) | KV store | [Grepsr](https://github.com/grepsr) | Watcher for [Apache ZooKeeper](https://zookeeper.apache.org/) +[Redis Watcher](https://github.com/billcobbler/casbin-redis-watcher) | KV store | [@billcobbler](https://github.com/billcobbler) | Watcher for [Redis](http://redis.io/) + +## Role manager + +The role manager is used to manage the RBAC role hierarchy (user-role mapping) in Casbin. A role manager can retrieve the role data from Casbin policy rules or external sources such as LDAP, Okta, Auth0, Azure AD, etc. We support different implementations of a role manager. To keep light-weight, we don't put role manager code in the main library (except the default role manager). A complete list of Casbin role managers is provided as below. Any 3rd-party contribution on a new role manager is welcomed, please inform us and I will put it in this list:) + +Role manager | Author | Description +----|----|---- +[Default Role Manager (built-in)](https://github.com/casbin/casbin/blob/master/rbac/default-role-manager/role_manager.go) | Casbin | Supports role hierarchy stored in Casbin policy +[Session Role Manager](https://github.com/casbin/session-role-manager) | [EDOMO Systems](https://github.com/edomosystems) | Supports role hierarchy stored in Casbin policy, with time-range-based sessions +[Okta Role Manager](https://github.com/casbin/okta-role-manager) | Casbin | Supports role hierarchy stored in [Okta](https://www.okta.com/) +[Auth0 Role Manager](https://github.com/casbin/auth0-role-manager) | Casbin | Supports role hierarchy stored in [Auth0](https://auth0.com/)'s [Authorization Extension](https://auth0.com/docs/extensions/authorization-extension/v2) + +For developers: all role managers must implement the [RoleManager](https://github.com/casbin/casbin/blob/master/rbac/role_manager.go) interface. [Session Role Manager](https://github.com/casbin/session-role-manager) can be used as a reference implementation. + +## Multi-threading + +If you use Casbin in a multi-threading manner, you can use the synchronized wrapper of the Casbin enforcer: https://github.com/casbin/casbin/blob/master/enforcer_synced.go. + +It also supports the ``AutoLoad`` feature, which means the Casbin enforcer will automatically load the latest policy rules from DB if it has changed. Call ``StartAutoLoadPolicy()`` to start automatically loading policy periodically and call ``StopAutoLoadPolicy()`` to stop it. + +## Benchmarks + +The overhead of policy enforcement is benchmarked in [model_b_test.go](https://github.com/casbin/casbin/blob/master/model_b_test.go). The testbed is: + +``` +Intel(R) Core(TM) i7-6700HQ CPU @ 2.60GHz, 2601 Mhz, 4 Core(s), 8 Logical Processor(s) +``` + +The benchmarking result of ``go test -bench=. -benchmem`` is as follows (op = an ``Enforce()`` call, ms = millisecond, KB = kilo bytes): + +Test case | Size | Time overhead | Memory overhead +----|------|------|---- +ACL | 2 rules (2 users) | 0.015493 ms/op | 5.649 KB +RBAC | 5 rules (2 users, 1 role) | 0.021738 ms/op | 7.522 KB +RBAC (small) | 1100 rules (1000 users, 100 roles) | 0.164309 ms/op | 80.620 KB +RBAC (medium) | 11000 rules (10000 users, 1000 roles) | 2.258262 ms/op | 765.152 KB +RBAC (large) | 110000 rules (100000 users, 10000 roles) | 23.916776 ms/op | 7.606 MB +RBAC with resource roles | 6 rules (2 users, 2 roles) | 0.021146 ms/op | 7.906 KB +RBAC with domains/tenants | 6 rules (2 users, 1 role, 2 domains) | 0.032696 ms/op | 10.755 KB +ABAC | 0 rule (0 user) | 0.007510 ms/op | 2.328 KB +RESTful | 5 rules (3 users) | 0.045398 ms/op | 91.774 KB +Deny-override | 6 rules (2 users, 1 role) | 0.023281 ms/op | 8.370 KB +Priority | 9 rules (2 users, 2 roles) | 0.016389 ms/op | 5.313 KB + +## Examples + +Model | Model file | Policy file +----|------|---- +ACL | [basic_model.conf](https://github.com/casbin/casbin/blob/master/examples/basic_model.conf) | [basic_policy.csv](https://github.com/casbin/casbin/blob/master/examples/basic_policy.csv) +ACL with superuser | [basic_model_with_root.conf](https://github.com/casbin/casbin/blob/master/examples/basic_with_root_model.conf) | [basic_policy.csv](https://github.com/casbin/casbin/blob/master/examples/basic_policy.csv) +ACL without users | [basic_model_without_users.conf](https://github.com/casbin/casbin/blob/master/examples/basic_without_users_model.conf) | [basic_policy_without_users.csv](https://github.com/casbin/casbin/blob/master/examples/basic_without_users_policy.csv) +ACL without resources | [basic_model_without_resources.conf](https://github.com/casbin/casbin/blob/master/examples/basic_without_resources_model.conf) | [basic_policy_without_resources.csv](https://github.com/casbin/casbin/blob/master/examples/basic_without_resources_policy.csv) +RBAC | [rbac_model.conf](https://github.com/casbin/casbin/blob/master/examples/rbac_model.conf) | [rbac_policy.csv](https://github.com/casbin/casbin/blob/master/examples/rbac_policy.csv) +RBAC with resource roles | [rbac_model_with_resource_roles.conf](https://github.com/casbin/casbin/blob/master/examples/rbac_with_resource_roles_model.conf) | [rbac_policy_with_resource_roles.csv](https://github.com/casbin/casbin/blob/master/examples/rbac_with_resource_roles_policy.csv) +RBAC with domains/tenants | [rbac_model_with_domains.conf](https://github.com/casbin/casbin/blob/master/examples/rbac_with_domains_model.conf) | [rbac_policy_with_domains.csv](https://github.com/casbin/casbin/blob/master/examples/rbac_with_domains_policy.csv) +ABAC | [abac_model.conf](https://github.com/casbin/casbin/blob/master/examples/abac_model.conf) | N/A +RESTful | [keymatch_model.conf](https://github.com/casbin/casbin/blob/master/examples/keymatch_model.conf) | [keymatch_policy.csv](https://github.com/casbin/casbin/blob/master/examples/keymatch_policy.csv) +Deny-override | [rbac_model_with_deny.conf](https://github.com/casbin/casbin/blob/master/examples/rbac_with_deny_model.conf) | [rbac_policy_with_deny.csv](https://github.com/casbin/casbin/blob/master/examples/rbac_with_deny_policy.csv) +Priority | [priority_model.conf](https://github.com/casbin/casbin/blob/master/examples/priority_model.conf) | [priority_policy.csv](https://github.com/casbin/casbin/blob/master/examples/priority_policy.csv) + +## How to use Casbin as a service? + +- [Casbin Server](https://github.com/casbin/casbin-server): The official ``Casbin as a Service`` solution based on [gRPC](https://grpc.io/), both Management API and RBAC API are provided. +- [Go-Simple-API-Gateway](https://github.com/Soontao/go-simple-api-gateway): A simple API gateway written by golang, supports for authentication and authorization. +- [middleware-acl](https://github.com/luk4z7/middleware-acl): RESTful access control middleware based on Casbin. + +## Our adopters + +### Web frameworks + +- [Beego](https://github.com/astaxie/beego): An open-source, high-performance web framework for Go, via built-in plugin: [plugins/authz](https://github.com/astaxie/beego/blob/master/plugins/authz) +- [Caddy](https://github.com/mholt/caddy): Fast, cross-platform HTTP/2 web server with automatic HTTPS, via plugin: [caddy-authz](https://github.com/casbin/caddy-authz) +- [Gin](https://github.com/gin-gonic/gin): A HTTP web framework featuring a Martini-like API with much better performance, via plugin: [authz](https://github.com/gin-contrib/authz) +- [Revel](https://github.com/revel/revel): A high productivity, full-stack web framework for the Go language, via plugin: [auth/casbin](https://github.com/revel/modules/tree/master/auth/casbin) +- [Echo](https://github.com/labstack/echo): High performance, minimalist Go web framework, via plugin: [echo-authz](https://github.com/labstack/echo-contrib/tree/master/casbin) (thanks to [@xqbumu](https://github.com/xqbumu)) +- [Iris](https://github.com/kataras/iris): The fastest web framework for Go in (THIS) Earth. HTTP/2 Ready-To-GO, via plugin: [casbin](https://github.com/iris-contrib/middleware/tree/master/casbin) (thanks to [@hiveminded](https://github.com/hiveminded)) +- [Negroni](https://github.com/urfave/negroni): Idiomatic HTTP Middleware for Golang, via plugin: [negroni-authz](https://github.com/casbin/negroni-authz) +- [Tango](https://github.com/lunny/tango): Micro & pluggable web framework for Go, via plugin: [authz](https://github.com/tango-contrib/authz) +- [Chi](https://github.com/pressly/chi): A lightweight, idiomatic and composable router for building HTTP services, via plugin: [chi-authz](https://github.com/casbin/chi-authz) +- [Macaron](https://github.com/go-macaron/macaron): A high productive and modular web framework in Go, via plugin: [authz](https://github.com/go-macaron/authz) +- [DotWeb](https://github.com/devfeel/dotweb): Simple and easy go web micro framework, via plugin: [authz](https://github.com/devfeel/middleware/tree/master/authz) +- [Baa](https://github.com/go-baa/baa): An express Go web framework with routing, middleware, dependency injection and http context, via plugin: [authz](https://github.com/baa-middleware/authz) + +### Others + +- [Intel RMD](https://github.com/intel/rmd): Intel's resource management daemon, via direct integration, see: [model](https://github.com/intel/rmd/blob/master/etc/rmd/acl/url/model.conf), [policy rules](https://github.com/intel/rmd/blob/master/etc/rmd/acl/url/policy.csv) +- [VMware Dispatch](https://github.com/vmware/dispatch): A framework for deploying and managing serverless style applications, via direct integration, see: [model (in code)](https://github.com/vmware/dispatch/blob/master/pkg/identity-manager/handlers.go#L46-L55), [policy rules (in code)](https://github.com/vmware/dispatch/blob/master/pkg/identity-manager/handlers_test.go#L35-L45) +- [Banzai Pipeline](https://github.com/banzaicloud/pipeline): [Banzai Cloud](https://github.com/banzaicloud)'s RESTful API to provision or reuse managed Kubernetes clusters in the cloud, via direct integration, see: [model (in code)](https://github.com/banzaicloud/pipeline/blob/master/auth/authz.go#L15-L30), [policy rules (in code)](https://github.com/banzaicloud/pipeline/blob/master/auth/authz.go#L84-L93) +- [Docker](https://github.com/docker/docker): The world's leading software container platform, via plugin: [casbin-authz-plugin](https://github.com/casbin/casbin-authz-plugin) ([recommended by Docker](https://docs.docker.com/engine/extend/legacy_plugins/#authorization-plugins)) +- [Gobis](https://github.com/orange-cloudfoundry/gobis): [Orange](https://github.com/orange-cloudfoundry)'s lightweight API Gateway written in go, via plugin: [casbin](https://github.com/orange-cloudfoundry/gobis-middlewares/tree/master/casbin), see [model (in code)](https://github.com/orange-cloudfoundry/gobis-middlewares/blob/master/casbin/model.go#L52-L65), [policy rules (from request)](https://github.com/orange-cloudfoundry/gobis-middlewares/blob/master/casbin/adapter.go#L46-L64) +- [Skydive](https://github.com/skydive-project/skydive): An open source real-time network topology and protocols analyzer, via direct integration, see: [model (in code)](https://github.com/skydive-project/skydive/blob/master/config/config.go#L136-L140), [policy rules](https://github.com/skydive-project/skydive/blob/master/rbac/policy.csv) +- [Zenpress](https://github.com/insionng/zenpress): A CMS system written in Golang, via direct integration, see: [model](https://github.com/insionng/zenpress/blob/master/content/config/rbac_model.conf), [policy rules (in Gorm)](https://github.com/insionng/zenpress/blob/master/model/user.go#L53-L77) +- [Argo CD](https://github.com/argoproj/argo-cd): GitOps continuous delivery for Kubernetes, via direct integration, see: [model](https://github.com/argoproj/argo-cd/blob/master/util/rbac/model.conf), [policy rules](https://github.com/argoproj/argo-cd/blob/master/util/rbac/builtin-policy.csv) +- [Muxi Cloud](https://github.com/muxiyun/Mae): PaaS of Muxi Cloud, an easier way to manage Kubernetes cluster, via direct integration, see: [model](https://github.com/muxiyun/Mae/blob/master/conf/casbinmodel.conf), [policy rules (in code)](https://github.com/muxiyun/Mae/blob/master/pkg/casbin/initPolicy.go#L21-L95) +- [EngineerCMS](https://github.com/3xxx/EngineerCMS): A CMS to manage knowledge for engineers, via direct integration, see: [model](https://github.com/3xxx/EngineerCMS/blob/master/conf/rbac_model.conf), [policy rules (in SQLite)](https://github.com/3xxx/EngineerCMS/blob/master/database/engineer.db) +- [Cyber Auth API](https://github.com/CyberlifeCN/cyber-auth-api): A Golang authentication API project, via direct integration, see: [model](https://github.com/CyberlifeCN/cyber-auth-api/blob/master/conf/authz_model.conf), [policy rules](https://github.com/CyberlifeCN/cyber-auth-api/blob/master/conf/authz_policy.csv) +- [IRIS Community](https://github.com/irisnet/iris-community): Website for IRIS Community Activities, via direct integration, see: [model](https://github.com/irisnet/iris-community/blob/master/authz/authz_model.conf), [policy rules](https://github.com/irisnet/iris-community/blob/master/authz/authz_policy.csv) +- [Metadata DB](https://github.com/Bnei-Baruch/mdb): BB archive metadata database, via direct integration, see: [model](https://github.com/Bnei-Baruch/mdb/blob/master/data/permissions_model.conf), [policy rules](https://github.com/Bnei-Baruch/mdb/blob/master/data/permissions_policy.csv) + +## License + +This project is licensed under the [Apache 2.0 license](LICENSE). + +## Contact + +If you have any issues or feature requests, please contact us. PR is welcomed. +- https://github.com/casbin/casbin/issues +- hsluoyz@gmail.com +- Tencent QQ group: [546057381](//shang.qq.com/wpa/qunwpa?idkey=8ac8b91fc97ace3d383d0035f7aa06f7d670fd8e8d4837347354a31c18fac885) diff --git a/vendor/github.com/casbin/casbin/casbin-logo.png b/vendor/github.com/casbin/casbin/casbin-logo.png new file mode 100644 index 0000000000000000000000000000000000000000..7e5d1ecf95b4f0a47e297dc1269b2faa309837d3 GIT binary patch literal 34267 zcmeFZ1yo$iwy@i{1cHSi!QE-xU4v_|;56>;?!h6!-3bl>0>Le~g`mM5f(Mt^*}~4* z=bZoMzvsU9#vLPT4Cu*PbIz*zs%lo%?54vM`fS%xVX3&fy|7|%=BOldIvXaM}1d%YX{Qbo&4!X z6y#uNZ)WRgW@AnC-LJlZjguoE3CZ_@9p}su>wFf-FH+AZtelupQICw6is}akOzTwfQ$iK0N-n1%vxUR`#Lne_3BE ztAAOvgQJ);7{MP%|D~maikmHnQ3>Q=<796L5_1MOh4f#w-oa4`^jGivge$Q6zjWki zX8dpbe1Gz9A%I-X{*Br9C%>EhHl}{>YF;ojZV`KszN3x3ij9pW|L?<9;m^Jx5)mPy zQZutQvT<>s=4Jf5_FrS>FGnCzeMb;KcuaH91KH?-tSZdR+$=zDCU#mNCpQrIgOlGi z|K>y1#>mXr?Vo(GaI-LTGco<>;|I;Z`vA{DBYj8xpYrmf?hk&947rVM?5*@2`OU2K zO+bvc)+W4+f8G2+^I>Fii`ZD&*n_JD;%DJy{8zVsu=;JZazD3raMZUp1U(n!2j5^Y zGc)35(&q#LSq+%!LB^cM^qkDBO!NjuOic8K`kYK40}zPA*uda7h_bRjn*Wn)Q5!?2 z?=$WX*G6F1tVSS^F^eIPo`aLgn4XK3m6hIrL!XNt1Y|bmVg~B7a~T``>G}ure{wBv zZw8K*`j$WFe6RiYB8)-ITpY|mV|sS5Lwa@=R%3AOjg9G2w z?B~`u{2mec4ZqI?5SYS0-2JOs50-zJnf-GI{v3ke{r_P8@5^v81zG=}Ce-h)e$)E* zP8@8E9bNS8K|&_rG4Wqa^WPo*qs8An^D=&4JS@%rU+4n|edqtI2RIBttn4f-Ec76D z@M6f$#>z~u&jA9DNoE697NEYdp^*{$pFRBF?EzMHZdR5*!_ogQJ@D7qH8jNF!34rVq^W+QqK3yT3gy8$?IaIte3(i`Xl4fKscTx^Ey9Dla&zuReS zOx!G7zdFtFA==qkJA>^1Z{i&|s_KKcxFCB6eq(zZE24+Rk;u`8NZ;1h(#%l*dmv?W zwl?}>!}zce{@Hs!`1t>DfEt19&HiuV$`7?P{1X$=|FSm!$C32sb`1a7TJ~#F{^J(p zKkhU(W>!`mp=FxIFd5cbFyJI`yodCuZ*Psd)oXTPf8G*5eR&QGoWYX;$)>~0Uy5T^^J{L z=s7u!nGJ!=AZGBa{j*ztP0H`btlv&~|IuOYKkl@jJB$A(%irJf8vnZw62G4b|M6h{ z=VAY!5AxqrM&KNV=JA*-);XgRkb#A0!RmXq^Y?t5GC0Ez;%8@LV`1ZE{QK>{TPT>hf-KcV&A^E>hwtei7EW+b{>|v` zPyVH)#$T3P;0(iGhCe*`!|?aH@ewTsq;cbQ=y6qwV^gl2B)uR7- z-wz&t74m@MXM|t4eumP6&M#aKD1Juxh3jW1J?Q+x^?>4MgkQLRhSG!1FI*2Oen$9( z>t`rE=={R{ z_=W3dC_U)>!u5dSXM|t4eumP6&M#aKD1Juxh3jW1J?Q+x^?>4MgkQLRhSG!1FI*2O zen$9(>t`rE=={R{_=W3dC_U)>!u5dSXM|t4eumP6&M#aKD1Juxh3jW1J?Q+x^?>4MgkQLRhSG!1 zFI*2Oen$8|;zIoEi+e!U;BV`30e>wI2O35=_$z&g3?-Fh0RVS$0Khi@0JyvdzitBn zPCx))M-KqtP67b1Y~pmg#Q^|5hv%X~Dz3A8=`KkGs?EIuCjE1D2gX|Nq<*N7#KeMv zCBFWw0Yo&2Ikr_E-~3;{vd6^iwh$_HL{kjS$vL$z_5&yhqmO&k9ke*@y!T0)F~V>@ z_o@d;Z=`N|IoGfpBqy_M-ngSFNeKtSqmgvo!R5xpd!Oz_*%pXn_gn0!-z_OwUkzS& z_Q5md0Bsg{pEBymFelG{U$?Ar!=R}8hJ|8fvAA4RlCpS+5@Z60{rG17Mwx0^e@Yag#)pvE9o9BT`Tp!wVHcO zX+&lU$OWwWK5v86!&N}+0^oQ$5_M*0|~%7@8Uqm4w59|)HO`3dqhjOP@bGwh(AavRbe z@&`yu_(aiAR1v6=lOz_C$0Gvwp7p)7G&Cfyp3*l~7Zt(#20g;kxtwZZWRitthccMwbHrXTP|?t6^7`C)lElKtN(OeKWXhYFZO=&H z*>xdkkCYV`>-Q&6XP*j);|Lgra&zbRmH*pLC+@Q?-zM-ao> zY7!m!AOa;uy>R@*50Uy&(7wABgcp*Qe&nz@OpY4~3G_JId-UqnE0koP>1;A|gthSA zm}fc^1kp9N%aHv81M(Sc7N7gFR&F+j)1|=|HUw)Ac3YyKPd15%IL(fqhu=YP8vw{BoQeg4eswDaknhK9zV zCj!SbMO?jI?~rUR)|U|Co-2Igt3{DyUNrg!dLVoioXkWQ5GgOADbJBu6p8AH*QR=v zvxX&k@RSSO%8HT_ZqMtpuW;TMorDFY4e+}|61I!pPf3^tofwsPZ{g**x5?hnYdO#Q z!#v6;E-Uk@DK6f&(w^eL2|g-7HF$+eLK|2qTmWRo@qLVkdb8jc-hSZtCAH1QH#)N8 zJrCT)qe^Jg-Q#o3EZ(HKp>~!Kc%~|h{i_l4SIEWB?AF3+Li<28neA#8xm$KVuua<{y1`J z^?K_sD@M%kIn(9hrSd--*Sug(9@KpEZUz{E2@VaZufoF@(1qz>G&|RsZjhE-mr~ot z{D76p9hwj}5LycC_hWea4(NxRwpa?Z%X$zjevNvDEf6=1wh|4WgpttYpoWEmgyoTv z+MZ@Xs6W1W`Vn~(xm>&HO&A8{0;kz{o{O4_itWX6i1N#qG9VE7`Z_4$Z8_+r6Y}fT z9>o_=)QpMSG*9sMk3Ghu-a>@c>{;)vGE(xNNHTml6b6iQrEY8(Yo_3u*;qu@)z*q? zX=x4cjqn()_rFee3NCd$;%0}o3eY6Q& z0*)j;=_Mu0p~yIxh^@2bySJ6asD~T>xCpoFuO#IN72Z$NiTUCF`Aw@ZsKurG!_sCq zRs5P&r&n%dWF*I%#6-ekGtIsklguGMID+Z*A{5h$@__VxUm04~@^-g1N_bvYZ2;t= zOV#}mKIi&L=&S}RBc*Xq5^r`!ABOq%PKTwn^+t+CIdQU#Gx(d5c%L!6UIqcUZC^tx z!wV_Sua!m0erSG|jI&Cc%$1EkcXZerIwU7W6`pKqZfepU3`;TfhRH^gemiaAGD1KM zgkwVxv1`^S$Nn1hm3EmM<$RaNb_u>=WI7y^Hsg@@Ktj1tu6d&%)q16$w!)|*5Md1u z>+Y7LH1RHCam;tI4!`=VsrBP!qp_9sCECU;@_xHTcO;J?2T4-cFvQw%&D!vzuBW=t za2b-HM$N!AhZDGfpj9hRc*bsdkqMrwyvU+{Ksi=IdYl|P6TktcWWTgQ>Fj*hc;Vv8 zdrU3$ckh!$>HJ5bgJOKfhC&zw;Gl9+GjmX;VkEGn}IAjdcwwNdGv`D955yY>7&IrIf>%V%ORH~N-*eqq(hk3w>ZA98e)-6%11~37 zj^_17<8JwCabbqRmqw>n%^Kq&j3+NJ=(rIa*6X8R4~G&u1GoU8CF z)F5g7Q)@hhLTF8$ST+6SL*B-oL1#SK%JCT#1T+HVg?p5zOzmp5r?zcj-Y||LCXo?C z_mvKf!O81=&Z8N}fm@7+&#nystio~^w;Z6+(vYM`9&Qr840}p84h}z9@z~btf;=%A z@9o#iz*lt3&GEoXuWysJ_4S6IhqKmd&d~%eJ1@$kPa*hl;W_!0`oA}BTQP5!gq&>n z4zfG(OPB@|0d3-H<8DhQcrou8PUkt$0MGbLNZ2Ik4%TITBfzd72bq7JO4NI03r^>K zqn-X&)b!zT@94KvP0jMQ5%oJF=WBW zq%PR2b~SFtX(P*InO@G#;N3dmoD4Bm1xL1?q*l|onSe`SEjUh~FqQ;s6J ziu-*$%)Ed?pMcIwDLW8#z2~=8-t)w{qfTxrVsQ#y6{B&vEW61PRSsk3WUNfr6@>3W zHf~@Yh>@S*c`A-h`O^P@(={XL)YEeWB}wqGX>+xS%M(G&L`UohMnI4S$#Zd&Qs9>Q zeDR)Rc5d$eo%ijPmY=bnhpm->Co&9tM^M-BSqAuJ1^NV;QK~Jm+~=pQVWN8ppkRY1u0_5%?%AvV?io zW`!`Uo0@@fGcmI%28*zP=jHorN-5t&h4oR7j(T4U@sGE-MU`8coj~K^6t>-_I;)Y3 zEeU~swbnB>Rm6_r@#EE+N?i}=nmp(+vE0O>=KVVIFnkvXOttwSMpo7(N`XrRjxUZF z<-7bf)?XSAB&l7XyINzv=x^kTfS%>zx9dR4ePTVT-M!9pV26hOz_GHpVVcL=e!V;4 zX=D)Bm?X^&9AVXvT9J3dO^F$#c5X0I3P1m3Wa0 zXCeU!g<~t`tTVVnb<9kvVxDh46*ky<;>NJf;s$TsitRKKr8`tUIns?R0fHO9Xm<8I z;q}Sek&rHdt|I?OtPn%xyX9I<&R<=8BU>-J85>TbcoGz_!btlmmo%2#`7;hy-51Ii z`9^w&{9bV3K(6@gmen7x65b~{a^D_becQE(QQAj>_c<{oy)&GI{uDOlhrvo*e#C3G zZdu=Zh2QA=SfcX`hA<_@Sn<7BeA$H2?zA7h4pzVC>T}8W{`ha?nsV)gL2qg-Sd4g) zdk+@y$N+O8PbF5HCp8v$7({vk(%*=Wqq)ZMl`q3;b~q0#Wud={mXkr7ATN^}kpXXN zhMkXi3AHfC3D?HOc_wZppp^9*iUgwUQYkYq8eZ{D+ufI-Zw}Nalv+0^W zrD@4NduKQ^U52w)#~QV|^8C=T(xr4}9pn41bGpuI?oB$6%ia9>Z6w694O#6q3NjSc z_&}(+o#Pl|VJ@7fG`!qI;Pfp0GBh3`bITIcH)>NKXd!i`(GxXIN_LDzzqDTgdFjB|`DNp2`h(Sj(@P$7s|bz11fLtoBCto_t7K<$R`CrtN|n z?D-fUsv&>bTt`;GPbjeHO;)BKtUToX))@tK?KJMIeuqJNr|CGhCw^0HPWY3wwdHS$ zl?$2mJD|b)Gbj8&R8wI$j})u)+yMub6n=aUG*je{SN@0-t4ZN_vUS05 zOE?0?$k!^RpnK)0;f0P)d0yvdt2O6~27b}UJ{+onU%gcP?9>kX; zCsN?k185x+FwHX+lf)9%kK!57g(eM@DoKb|;E=?-XEF#$6F#14GQFHYu}f7Y0EvOf z#l*zy=IW#^=N?<#iIhPkY^!c{B69WG4kzs-O1qv{0aX#V~5Q*(9y+{eeKVyw{a-#M`pM+EPa~H6p@FulBWuSLJ@I zueAGK640={XV_Wp`+_E!PQSCWLwVdPaK}XWev3p-VATt+`2w5&c=^$oV*mmMr6CV{ zFa>z7hsF%jzN@++Xl{R9R6TZ^c;}UwV)mNXdo!|@!xBZ!ER+dqpxQiVjB3@NJ-Vv+ zB-wVzFd^L$CpM0Tb!j<-aN`@tf*{;r|B*_|!GKJ|88HbP3q(;#DG5QeMaE_ja>^MD zRE#7mtJEnXZhBd1{X&)st=Jj;r#kqett&W)>5Mj1VAIF;W0;Hz%cE{`aut=R)i`N} z4XoCNu&+GMhpCG-Jm`I#Y#&m9!^NuAJJ;Qy6nxSC{ z@(rCv$MrC00ByyGxXHI_P^g)_(H|isNb>VaO5)V%f;Zh*I&i+gA?YO=h09mzh=0(#kAIvaPWk-?QfD_ zj^a63n@mhp*3xQvl`2_$gD|V}TuTWN8eO?^*LSxOe0r-koMX3c+!i+;$rxJei%;VB z;x;A|W_`N4ZIZEkHg}L!&_Xcfq-tDUsAw$NPkL7_v)o+9A!ygC8(O~l{xReHy@Y1H z&0{sw(5m2BS6(?;SvFxRtD9t#%=Oho&xo2~?pNU42W7kr1>tP1*@W_xu1~W^KXu{Z zWQAuChgxads8b-Sike!yB-v8B*pculnim7CTB+)x`IVg`(s0oY^!T}5o-D*WKCiRz zv9B_xR7{>ELgw3Uhe&B6p!m(RWHC*f93?w`NIDTZji#N z8$9b8&)t5tNpt2(HR9dD%;sVK+JeXZVBC3Y=BBPIg!U9Q4Rw3}y87L*y^UiMy1+>k{`@kHz~twAQ9eV}_Os1BT{PB`|B*CD9DJK=`JB zuhOtYN7Q4q8gH&BTMjvER_p^S^atp2T!K1sQxA!C;bpR`ZSjlCN0Vt4R2AP#Ycf(( zXTQr2%9lj)#xTf_q68YTOUQ#GeIG4A>e z0}L@XhxL3p^CNJz{hlt1ES)~}4a*C0qTDt~N;q&!n0jq@@>(r>+g*K!xJ%s-;H14xUthf;jMMmPWdOW4fxUrtqOmRWBhVPS;?mTH9 zb$M18LF882_jxSP9@ioTyc3_(aP1tkbpPso5tc*?F8LjbxqwhA^I zojjj#&+bx8j*FdwCmK#Gl)O7%vhU5lb=H@$kC*;|41I1iybj=8hfFGe!1cjBIHz_& zoPz7TsWOzr6wq{kN{LXwqHxRZ@e0oS)o5KiKE z^Ec}Ufw2O_Jv8GPn`KVgQ_uv^8OL4%n5cJ8Rm5#94t>yWS|N*sHEaoCbO2E`bi1s? zft??ze41OC0L~g*Qr~D626+c|lTA45cP0iS2?Meb=meVUf%ASeXguY+@C6iR4y<)+ zjvKG19UW6Npi8x`*$dO`X+{Ljkz5w+C~eAK4*A2Jyx+=+R?^k&O__ZAL?2elvF~$Q z`{y*du;l~&j3aU~q)aP~^hlg^s8{%PTHIr%3HWn$OrRZB0!xo{Gu-ZAC?YC0 zZ&1=k6eS3UHX?VORkIz)RkjIi3T0C))9oh?hLIA!zLmlizYCm3$$Cx=-W}l%C?vNN zG!1ap!gv(EJ)Q2u(}WUNz(Boeg&4?Buqv|jh^)NYY%vC;i_RC$7j?hUAh2vo-{c|K zqShGPj{=Ib0$E+(llEtmvomRzY~c<8ct4kE3?)2|Z`hcdGMIZBE14ie(>GhMW|&0L zGCCS7%G!5Q#+3J@%h8rM?BKejF{|J$`Y2?E%_}?Gux9DGP*D=g*;>Dy9bEqNAV&Kj zD?@RayxmIuuXC;~(fZgI=p>LNDQl4`&;mebr#&v|lXzznmLg{9v}$nco+KFue*XMIQ%h^q zN!4Qh+>2GV6MnxPj-rcK=OFaZID|&vOF-e2aDFq^9Xc$>u>@3a$4P1VX} zT>qgOg2@8=Az*1!avO0Qnaavbyve0`c(M*TkHybRCogPUNg*(NJN=5=)5K@+qZ*iq zRCxT5+i2SlNYR|YDe}iO5++K5N%gRz@6pu#sw-TO5R(;l~BKeM=-Cai!1%v2G`(8sfdEct3YrgQKGk{!n4+xy#ah@$!?9u2zndI$Ll zWSPj}<%VkVPsz%rPS5xx8fOfev7z(6kTs6Rz&mCQ#_&!jWYMg!y!{AD&0oGKDUhcm zRX}fs?-0u#Jf!CE$}o#OQBY1`)?Y;kxPscLjN@{)ei@Ausl<~1c!PhajYz}*>a$&c3WOUZ(Z83t}_`Gdjexth$yg-5lJtG#K)kG zx-F<}-nXsi(Z#~B-VuL*IoaJ6YVkNmz%>u1>VP+7iJWKlGy$-r&`$t1!a_F(c5?hA zP0yMV^P(9ULdXlc;h8$u;58rmhY)$dNgtua0pzZ4qMvTgj@vgZ?@Go}UmRDDv21w& z7WA(3OWUs-zL)b)Ke*uRRj{i}RSb*Xdn&p-Qs6@`@psvVl_|o!u&%uG$c-(8!h(tL zanG;-`PKWG_}#<2h%|jy8iPFxm8df=fu=&bX|i>wnH0|%o*OQ2qPeHnAF(3f*iej; z;{Jp%)A%i#h$(z_E~%LVnrx#^}+NvuW)lHFxGUwE63B_Ag8_**h+X+36 zH=@)SlwxkXeyxXX6+Xjr1DWJ0RK}lk4BbY0?*lX_Gx|T%o1Gf*$VRp6gBS`Y^QgzY z;>yK8tD%XePcgGfLQiR?GF_b1k>F7mv3gh`olpQP6ok#iN%Q9BF7ASQ&w4Ml9(%%H zAlZ%rru;Ecr=MX9-up8YK}Nyx6G+O61=JeTFYzxGww+uHO5KqhK0oU>IDo5$K)YUq z09N|326B;JTz7cu%t77ZMBI-Pjhtw->3VQwKu006#@)fcM}egLW_wfROYK~*D-Tr; z`xIp<{T}H;sBN$jD%3OtM*Kng=5_rOWMo~XFjJY z?z)YXs$0Ai1*fz4a`9PAI=XtUYF>#jYSji)!Pj17b&Re&R>wZ{OtTIOFXGywp4b)5 z3+~4ItYPThT=5`iDDWuH{)sRy$FHiU5)r8}%;L=pReKii=iVw`? z@=oEBmr$3H%1>4GdT3Z4xE?8QPM++J_=JVnyw9$P_rN{$S{fx*$2te7!Ju3EST^fI zGawH>5((S&tw0`|K7g>M6I|Iv;fK8OCS(G?uT}5&3hwBx6&qOH-XYfZwHa>&q8Ebk z%y4g9u#zh;WOcyT@QhhZ720DrKH$`o);D@wq+!RF8DMA{l9)sy^^S}B%E*2@|32~N zTCDNw*w|RxxtdShxqM^9Wqu+0WXZ<{f;WQ-ICj*`WQ_H@ACg&<(`<{QTt$Pr#72#G zE$d`v@zP0_l{E}j%(+9FJu!O4a4+@<_jf+0u0ht{ZZ*-B4ab&yp`;ez$;+BkK7U4F zDtQ>O%)+&=WOkJVX^9{k)l(25G&@lQ^fE{poI__;xO|io-@U%h>#-UFX3+^@DqkFad90ybqNWIK0}9Hj+Q(pw~GF^5DWn8dHxV(ZJbZz?3l zyxvN0n|{DYm3iLYbciGh^5&3m?k}aw&lH{S+B?$p2)&3##l?|=;~-Rsl4zEpWzDhF zTvs=^GQ^G=>>^&t8*N6h%^#mR@oL}6Z@k7If#`lz9Q6r(l7@;Ut3NOeg&J$N@dJsh zVAbPEw9uqc)3&_z)&k>+3CkpJ&cgItZvyRetvZf(`*tj#Zm~4So^3}{F}%}$o2qZv zJiAzmJ2s@je(UW!weM+N00=3tip0fXMZ&h$`Voy~G4YNQUFYPjH-nc(%3g@Z7vG4a zD4O+o4?_fxHMQF=4`BB}fS|*xFb7{<7^OA6r4nL_yDU$32Sw+Vc5#LaH#fnLr^N2T z(b=Aega(ee7QjU=DxXe;(&ae4J(p}x=-hA!JVM(Fx{_WKiMDsmONOwSje@h-Ax0^V zw<38J5j*Z(MC>h)xy*iZvANv#06FQDp|5Y%C$4D3#*|8Oz9mTPP_va3g?>Vvdd0+w zAtHL19}*I>t2Nycw4QG^_A-i&D>zrvC58@1UPS8+m-6n##CgTq5!RyKWI>rEHJ6?e zp9G;h(YM!_mX^=aU#YTKeOuh3%8bgV1b)Cn(2jxke4ftlTP|>&m5KMQd)Xo3a9OHravtUqNopZK*yl6nvcMs1z8yKOp+rE^O&JENev)GMyyu7FGk+J-fGx# zM(^y?@9vK&pFc@%Gr zOC@UW-Uq?)UD-&vQS$Z*fRncu(QUbS&}tdNhLr(u*l1~MiC_q__tsnQ#kS3Kz983| zM@-bbp*vS~+^E60Q2sgr;ReaU$75+y9DG233uIM+{Snv`wa|i5UqsDme?-g-KSZ0z z^JRCn+#FK^+7cYGT+E$hXk$2!;sTT@Eo{?60USOg!If3%h-2)I(b)2e zGe0Erb4O?WDH!1?)L?*!#H%g%2?ahc?XXOTPgtHYZ{GIMaEuKgNqr@e!b91pr=8Cwk=gk-M;kaxl$nWeZ7P6;8^pe5*^T@fgV%wg3 zr{)r(in@tQTGPb-1x+QZUW5tE_r%a@4ln*n^Y+)}K7KTV7bA;yRrrqrR|SLy1)`AtE+Zee~tj>j>YW$F#XxEMw#2)7B_ z9TvGc{8UM*gyKc@X|Gz%*RLFpa7LiLb(Y>`BIPA}F544S;#}w-x(U#v7(-kxTFQ^- zeSS5wfd!ejH4;MaD} zyqw*h(n%fuAecaZ5(P0I_oc$@WX8HQ6b!pr_F^DVc7jb10kkFE2kdC^*HPYQle3xgHD$^z(Kxk>%^un4>nJJ zGLJ9gX{_Wuw;p29Mb0oG0#cUEC;7Elmu{WRmYl+a^L}+`rHRnBxGt8+C)Z}RDv#tm zFkX1DB%i%tE|Yz7jU$DO!tzCwcOv460ad6^Vb=nwRPW?rzX;0lCqf_XkLt04=7S+7 zP!3TnzSnX&9W?SN@Zyv@ls-uWexEML`uV-x_0C+~P&dDA_P66!rS+;j6oDCXF{ z*9|yCk^@Slo-GsTBBaertSn+?3ac&+hG-OpR#o}px8v@k_}$_63nZXWW(`*6hC)Q> z!E2NH_Xayhr!i7R3KmlrMRZ&=^akhkZaAlsER(Q#RSxlMD}HW*C9*BWE4RXA{``Ke ze!C7%Q`kIa|30&*D@hm1Mai{eB)&bzdxQvWVBc26FR-YhXvE=${P>zqoa9AL+88bP z<0L`MWthh|(mep`nLy@md@R09Ei&HM-bku77Ai@;;=Fl*%oBy}>=$3(p!Xyb6vT8n zf?RZynVAB29s#?Y45d%L6uxLUsgJ?lUCbuog2mWXuv;TV9#DuHSTLVHEmXV+ARvtADHp0yFg?Z)a;h083Sr9hWs>r zcD2C1N7y&aixtZ1N6oq7%PIDX`fc*aD@n8;4|a9<)*dU0n=W#ah+G+6KAUIep`LLX$Vkla}n>mnU~R*o4Z>= z0oS#Q>ZP(z-hqDq|%13ai%8&%a|%*@e7A`DhZz?B_>d#v?X!2 zW)&L>>X648klmtsq5SObK>$VlxFfn3?^=gkP%f}rQ}4OnMDPU9pJC4BaT};ri5Fn-f`L&ZK*kXy*vqOciayA(N2iEdx(q{Ep4LJn zdh@Qx9z7S~h&ZjnbGC#f4TazGdrRO0*g3jO&Qxzkgc6S?X9$Vnr6%SVpmEb)yz@;+ z1tn|)?A$}4$0Ombka=A98S6Kn8RA~zCs)*rlxdMdZWH6ykIvFzWoh2sVg+m-64INB zzwX>Z2CbpI{h|w8c{Yh+j%{k9B`M-~Hukz<+#`fXjU>zzjk%@!YK^-*-HS^%(+MKk zC?(-_PC*gk)LDaJ@2ZMe|Dgm{IxX6G#f~>uLsGC@X;I)y?wL948bntPi{5EtYGig9 z#jK|T1Y`w$B96DcicaCfj7#%jMJ;fdLX&eoq3yEn&x$KQ9t4Cjx#Ml*Dr(^;BMCk~ zrO+5LD4Pt7ly26Ld#4dh|Ak>wNJaFcE7uFB(v534uM}F7^pMF|@~=kNiA5dJxM}q_ zbG)CAaYLTUcgHSlxy~t(1cho;9XBu{1?zkd|k!^|j#tTA?ZJ|7D>lrQyb zY{p6zeB|OPc~wFTd7$e}@AKv8iGwssSNz>p((~1-mdv#y`ECMdD4ww0(sa{0+ZIdBJm;RW$$u6ORQv^+?#Ls z7Vn+UKuv@EIJ83zqU6jZ?++&rsvNJC@szw#xpM&9KG=|aP3h3cv@eDuR8fFl>*EJk z;-~AP1^SrUI7taIr8vEVM{WZliCDJXb&8cOa$=Lv+zz4S=j~ksMI@&6qpjWpH31{N zkY!6IRGIDSLd3{66hV1Tj#&znm3t&TEvs1!th4j1V(tmjy9>>`2XN#R$D<$KauMnD zwkq#RN+;IG*_<>7@DFf2zKLPJp6=fhnO~bi)O00A<93%(z`;tyqw?%-nyz+ z+4pNL824zM>vz)@7QEW=-cJ(i2urBk#h}k`N0&)e@BIK(aK>[F1lucH={v$u@` zNr)%b)rwJ(wS)VG)t70dC`w}_$D9VAP25zRm^hw15L+5aaJm^`CgZdi7`}bTJpbk5 zl%RKq6qA?l-b03o*3Dt_{-}8-(Qa~&JTHzqPA+99$GkSF-HlOuK(}6?n-~N~63dtB z-DMsBDuKT-6o`I2!^4A}TFB5zRvVJn&zzV$ zWh}TP(2bP_q94Caoe-8l?DgCNA-1ntP>m|$$zoqw}lMSfR=?GCa|FK5Op}Nx|VEC3TOWO67i|f*; z1?M}@dW3I>aX5H7%ur-TrFg3=ulx5-#_KP4qwWoLE`=nG^w9)_NwiVq-4jwUw^G_p zv}^UBW5)|6y$#;b*lG!Q!Y$lLBN!6p=U3u^4;n4s|AHyE6Z2cJGbR$0#LH?mI(3kS#xuI9X!8({1R2=W4zob zbc5DF1komsO!O2Q8g5~$GxGWHEscp9!>qFHIbRQEBos^N83Nh8T#dX=pRV#3gS=JZ zQ7Wdl{?_`P>+nyP-X62az6)X2pHO~6ULQwaqQfwzGtKiv1p{)f`lCj(d0)^4pn#<^ zWj!E_>&XI#9$!Dx6&GmM0x{2m2#uWFrj=e8X?>; z1)@|5sSQT=Oc)UcXT(6b6j$OZ?R*2}{lO9~SB<@k#xw1yqx_Me3eV{LxwD8f>gm_J z(!6u+qogY0J0X0EGUnV>mMmqf8^>4v1I06!{nbaQ8Mg{u=F%Ql8N|12QUwnlss+ZYgMg+0q{<^gw67xD(E3gseHv5{qVq?yyPbb~3d(n^s&u-D~k zy?954!cK2uy-i2PpON78Xyp?O$!csBPtpr#cvWM34aOk$K{v6I*2+Apclh$Jl2Qwr zpUogEo0kURKRZDj*j3c1awn)6kZjX{Njqs`lDRpj@XmyKlatnbX}&^-fZa2!cxd|pTt{5SUAP(~Z+JqL5x3%hvsJlG zi_-VhOTW<`VD<|$^66M@b1R}a3*&|U)~476Fn*HdW^HVl*nzFQCCxc|VhYJEl%mBrhZ2WeZ$k7^nAN0bBoP(07<9)VT z4~`z(rF7qmZ1Wu?${fL#z~El&bUr7za1%uk@v(m8Gyk>V!(>D=gF9T}9x^Cbc-g$$ z+DJ#wz-%lT7tkTfR%_=A$l%9y@)Fgt)u!}tPw=HaKZ{+@funh?_2hHL{=B~T@XXrC zv6s8Rf^Iz~(HBbC%M^*n%O`ZyOKDi}OcXe6sluTg`LstY?80+XqG}|p$Q+(ObBCNIeb;`XvF8Vrz^h zv0p}T?vjiFS+4|0+pGpi*aR0PWCZ#KMo;rUmig6zU~(u)If<}bi^_E z#0==e?-;fB?s=i<+k(O}D~p_LFKLXMtmKKxf^lNzG8@cyQv(M(YKWb7AU$Y`M2bGd zG29RN4m=6d!UDoHRha5cwwk>orM<7`28BLU(E7fT7`sD<0hYcYZaj1GqlGgoji)ND zo8TjUjz53|aL%%Dd9J(i&iFNs5iR?1Dls=PpwyG zq7X0fr99y-Fm7r4te}M@iSsQENFweRQLug2$Q_T=wyzrKaabp)$V#j3Ri0xk1xzIr z3|SZ*H8@a5C&V4ab*kLZGwSSo&D!MZl9r+_DWWkdWnT)0tndI;BJ_SUME*cYbhEgT zF1LB}isyUc zzsa4CvY6~^ZmKMqr$JjHVINvm0Rf0RKveEE>s!jV68(gYOD`xT`bn^EjB(18y?fGz0~2+70?FErN{V{>72$)-*2;@*`>N)$$ms}k@C4xq zRn_x)a#a6Pi3eA5yhrSt8pad`E2uvE6`F?9?SUWbCzjqx$342WahP*Q`P{V3=u;v4 zCIZ!zOQ9v%v(x8EMG?cKj}rRhPcvpNfixz|4ZN#(FdyJ=&{fs$#TGQ!U71TTrqB^- znV7J*!cgT<;4fYLe4rz4xttTY_3P5M5S3Ws^G@cW$0Y>L{fbH@<1_>x(0jL3Z?O z++RRRwQIL!`0#;9eJG{0{= zJGDEaj-mmw4s*s*uPnH1I!vB7ve#vHK8cOxOsr7WLyve}mCS3xf3%`hUF;tpe9sdz zEzlU9xeK=@|HA!=>dt$GoHu~S+ncv9LulTdzY=n9QT5{F5V|YM5VoZb(1q1}baoP1 z^d_Qp0nTDofNGu>#{k+O%Q-;+L3^Hz)$d&urdd;H!H$b$q$M`&TV>xVg zeF-*77-iWr*4V{enQgU-3Yzx2XB2YRYnU=AEUgt&7tq(`CB{7cVVCESBdzGFi{4a6 zi$#?^gMgsKa@QEIL~TnbLc^_KYLPNlq*^in35E(fv#gKF=_EZ(Gv(6Ig>SM$e1m^YqoIj=Z)W>|X zjET*Kna3r;H$GW=qar~bieDcmo-OlmsWfa>1TT5N$Rbg1!M|yxi>A(Fe9<8bkvj+M zvM_QxM5;d^jl5$l3O)^TI@OAg*aY%xje7FA^J4 z^Bf_qFF+7j&7#aHF(_g@!SL24kwHe|$nK`7e;GI@zN~1HV#Gm zC`ho8)>GBoKdQi+L+(mdFw2J{G@=b6Lr@GoeM){(2X=5{up)j~@%>mEj3fo;7 zigw$>E8PdS>o^3W21lk;!b!#hD+u#qpk9~OgRAA-p<<_l6RuH?TZAu~T)un&fTQL@ zRd7$vGQV`)oNW}kCEUWT{TeBX&_W%@ceY3=qr5u;G@iKjnSF(|5KR$--kOGh(t{#U za~@ijsNQ&My7slxuvQw|CCf|RZGo8-i_T*@M}461QG8p(D2I9KfZa`y+7l)89LiEe z8DUCDft-6)>`a6fA1UDHF9ASGj@)Vs0r`6p?Kj~6ItksFQW)+jcTLHNkGhleD`;r0 zgu=WRAbgEt%W^9}e(dX$-C>87opwGWlr)7$<6gvLxi8f8Flna`=O9R1x0;6eQFxhEP;<%X{&q)Fj|mSW>l4|Be+*koiF)RKSB<&!ZFY{EU2TU zT)cDqfbzUogfYxeg?to)LU=;Z_mo_R8H-8)cl;aCS3NeT(a|ah4iYY(aV)NyGsrqk zdZRB$!N8EJZI239R-!bo7Ou{S92{6Re1YN$G$Fq1i7IZm$#-THG-RU-()J5ci1!{W zF~^HWrdCTNL?tzl2As&4?W;~)3{ZX7Wxl94O@J^b<^v=(8JI!CN`+VbMj4ocyGG6~ zmvP$r;DzbfSfyyoSw*2ufylpKM{Y0Qtt`LtMZ^T;h9`+FKw)8B+Z3YxB?j-vE2x!-kju}!l^-%qGm?h7ci8uDS%W^kWcHBFfS2LxjNI2L8QAh23)mhoJQZ>@ z)I4%C@fHsy?)v_|ZfM4qc;H3qQ#O9MwDrw2sGA66p>GNTh5-k;npEz!7JYE?sorL% zX;<&RM_zv{yZyE-In;*19mSL9*ol*E zv4GQkJ&7+}YZAdHABxrvc*34-nI(XV=EWbn>I)U?9W=@|`tGUrgzncvo(xk57I{K7 zcRah1>rbueYtch1N3o&t9kp@3qK}nNqc=QU8->^v1NNIlS!7k%%Np2JxsTB0S(v`y z-GTs(6wf7}iA;&-Z()P~PcH`Ii9akEiod4<+c!*HNLK2bTH6`34i6S(&b@Ad*zN&_ zh{QRo*KneBa^6^d)kkDXLtApN#i`e}nq3zpV)1L94ptao2HOk%QNQ^aq-NkmOL6rmLi#Pjmy79J7U?^5ZzLQvV;Wz%@pkTd@X2sBh6u4A zV(dAA^DuyPHp>(%!KIB;sjwp?W7etlF&^{^fJ>* zoq(zrlNh4;aE0`ntZ4Z&v24qh!(RB&XJWZ#JykXt$4H=i4iXt;Z-}oj2t}|W5tFu5 znlJzOX4Y=m5$=W%AwmYQ&#=#f>un@uzQNZ!>Zmn++G8n75!-C3KQAOlX@O5F8?V7l zc$Dw>_w%vhnb)W54sJKCR1BDi3SXNXd}>kQj`gT+Q3fhhE`mzLhyfNV4%-esPVoON zsF1y_s7%l`z)B~YXsO-wEb-*_RqZtgepaGS*F7r=!qNpBTq*u81-A``E|Rh=W=)+) zSxIrY6GDUtF}4{1fFT7D0gmPo0bFyso2IvTgel}nT>7LahkO&;J_$Gn4MM{4mh$;!K8gjXSv>YT; zl&)zcGg%xr48{l%BE){c095Rmil_J4Bhd3{fvk{-SqRwx^07V+o$iLPn~KNuF`jf+ zmou3I@j}2{GW4O|LX1?1;j}-62oWMgh~a}4PC= 1 { + enableLog, ok := params[len(params)-1].(bool) + if ok { + e.EnableLog(enableLog) + + parsedParamLen++ + } + } + + if len(params)-parsedParamLen == 2 { + switch p0 := params[0].(type) { + case string: + switch p1 := params[1].(type) { + case string: + e.InitWithFile(p0, p1) + default: + e.InitWithAdapter(p0, p1.(persist.Adapter)) + } + default: + switch params[1].(type) { + case string: + panic("Invalid parameters for enforcer.") + default: + e.InitWithModelAndAdapter(p0.(model.Model), params[1].(persist.Adapter)) + } + } + } else if len(params)-parsedParamLen == 1 { + switch p0 := params[0].(type) { + case string: + e.InitWithFile(p0, "") + default: + e.InitWithModelAndAdapter(p0.(model.Model), nil) + } + } else if len(params)-parsedParamLen == 0 { + e.InitWithFile("", "") + } else { + panic("Invalid parameters for enforcer.") + } + + return e +} + +// InitWithFile initializes an enforcer with a model file and a policy file. +func (e *Enforcer) InitWithFile(modelPath string, policyPath string) { + a := fileadapter.NewAdapter(policyPath) + e.InitWithAdapter(modelPath, a) +} + +// InitWithAdapter initializes an enforcer with a database adapter. +func (e *Enforcer) InitWithAdapter(modelPath string, adapter persist.Adapter) { + m := NewModel(modelPath, "") + e.InitWithModelAndAdapter(m, adapter) + + e.modelPath = modelPath +} + +// InitWithModelAndAdapter initializes an enforcer with a model and a database adapter. +func (e *Enforcer) InitWithModelAndAdapter(m model.Model, adapter persist.Adapter) { + e.adapter = adapter + + e.model = m + e.model.PrintModel() + e.fm = model.LoadFunctionMap() + + e.initialize() + + if e.adapter != nil { + // error intentionally ignored + e.LoadPolicy() + } +} + +func (e *Enforcer) initialize() { + e.rm = defaultrolemanager.NewRoleManager(10) + e.eft = effect.NewDefaultEffector() + e.watcher = nil + + e.enabled = true + e.autoSave = true + e.autoBuildRoleLinks = true +} + +// NewModel creates a model. +func NewModel(text ...string) model.Model { + m := make(model.Model) + + if len(text) == 2 { + if text[0] != "" { + m.LoadModel(text[0]) + } + } else if len(text) == 1 { + m.LoadModelFromText(text[0]) + } else if len(text) != 0 { + panic("Invalid parameters for model.") + } + + return m +} + +// LoadModel reloads the model from the model CONF file. +// Because the policy is attached to a model, so the policy is invalidated and needs to be reloaded by calling LoadPolicy(). +func (e *Enforcer) LoadModel() { + e.model = NewModel() + e.model.LoadModel(e.modelPath) + e.model.PrintModel() + e.fm = model.LoadFunctionMap() +} + +// GetModel gets the current model. +func (e *Enforcer) GetModel() model.Model { + return e.model +} + +// SetModel sets the current model. +func (e *Enforcer) SetModel(m model.Model) { + e.model = m + e.fm = model.LoadFunctionMap() +} + +// GetAdapter gets the current adapter. +func (e *Enforcer) GetAdapter() persist.Adapter { + return e.adapter +} + +// SetAdapter sets the current adapter. +func (e *Enforcer) SetAdapter(adapter persist.Adapter) { + e.adapter = adapter +} + +// SetWatcher sets the current watcher. +func (e *Enforcer) SetWatcher(watcher persist.Watcher) { + e.watcher = watcher + // error intentionally ignored + watcher.SetUpdateCallback(func(string) { e.LoadPolicy() }) +} + +// SetRoleManager sets the current role manager. +func (e *Enforcer) SetRoleManager(rm rbac.RoleManager) { + e.rm = rm +} + +// SetEffector sets the current effector. +func (e *Enforcer) SetEffector(eft effect.Effector) { + e.eft = eft +} + +// ClearPolicy clears all policy. +func (e *Enforcer) ClearPolicy() { + e.model.ClearPolicy() +} + +// LoadPolicy reloads the policy from file/database. +func (e *Enforcer) LoadPolicy() error { + e.model.ClearPolicy() + if err := e.adapter.LoadPolicy(e.model); err != nil { + return err + } + + e.model.PrintPolicy() + if e.autoBuildRoleLinks { + e.BuildRoleLinks() + } + return nil +} + +// LoadFilteredPolicy reloads a filtered policy from file/database. +func (e *Enforcer) LoadFilteredPolicy(filter interface{}) error { + e.model.ClearPolicy() + + var filteredAdapter persist.FilteredAdapter + + // Attempt to cast the Adapter as a FilteredAdapter + switch adapter := e.adapter.(type) { + case persist.FilteredAdapter: + filteredAdapter = adapter + default: + return errors.New("filtered policies are not supported by this adapter") + } + if err := filteredAdapter.LoadFilteredPolicy(e.model, filter); err != nil { + return err + } + + e.model.PrintPolicy() + if e.autoBuildRoleLinks { + e.BuildRoleLinks() + } + return nil +} + +// IsFiltered returns true if the loaded policy has been filtered. +func (e *Enforcer) IsFiltered() bool { + filteredAdapter, ok := e.adapter.(persist.FilteredAdapter) + if !ok { + return false + } + return filteredAdapter.IsFiltered() +} + +// SavePolicy saves the current policy (usually after changed with Casbin API) back to file/database. +func (e *Enforcer) SavePolicy() error { + if e.IsFiltered() { + return errors.New("cannot save a filtered policy") + } + if err := e.adapter.SavePolicy(e.model); err != nil { + return err + } + if e.watcher != nil { + return e.watcher.Update() + } + return nil +} + +// EnableEnforce changes the enforcing state of Casbin, when Casbin is disabled, all access will be allowed by the Enforce() function. +func (e *Enforcer) EnableEnforce(enable bool) { + e.enabled = enable +} + +// EnableLog changes whether Casbin will log messages to the Logger. +func (e *Enforcer) EnableLog(enable bool) { + log.GetLogger().EnableLog(enable) +} + +// EnableAutoSave controls whether to save a policy rule automatically to the adapter when it is added or removed. +func (e *Enforcer) EnableAutoSave(autoSave bool) { + e.autoSave = autoSave +} + +// EnableAutoBuildRoleLinks controls whether to rebuild the role inheritance relations when a role is added or deleted. +func (e *Enforcer) EnableAutoBuildRoleLinks(autoBuildRoleLinks bool) { + e.autoBuildRoleLinks = autoBuildRoleLinks +} + +// BuildRoleLinks manually rebuild the role inheritance relations. +func (e *Enforcer) BuildRoleLinks() { + // error intentionally ignored + e.rm.Clear() + e.model.BuildRoleLinks(e.rm) +} + +// Enforce decides whether a "subject" can access a "object" with the operation "action", input parameters are usually: (sub, obj, act). +func (e *Enforcer) Enforce(rvals ...interface{}) bool { + if !e.enabled { + return true + } + + functions := make(map[string]govaluate.ExpressionFunction) + for key, function := range e.fm { + functions[key] = function + } + if _, ok := e.model["g"]; ok { + for key, ast := range e.model["g"] { + rm := ast.RM + functions[key] = util.GenerateGFunction(rm) + } + } + + expString := e.model["m"]["m"].Value + expression, err := govaluate.NewEvaluableExpressionWithFunctions(expString, functions) + if err != nil { + panic(err) + } + + var policyEffects []effect.Effect + var matcherResults []float64 + if policyLen := len(e.model["p"]["p"].Policy); policyLen != 0 { + policyEffects = make([]effect.Effect, policyLen) + matcherResults = make([]float64, policyLen) + + for i, pvals := range e.model["p"]["p"].Policy { + // log.LogPrint("Policy Rule: ", pvals) + + parameters := make(map[string]interface{}, 8) + for j, token := range e.model["r"]["r"].Tokens { + parameters[token] = rvals[j] + } + for j, token := range e.model["p"]["p"].Tokens { + parameters[token] = pvals[j] + } + + result, err := expression.Evaluate(parameters) + // log.LogPrint("Result: ", result) + + if err != nil { + policyEffects[i] = effect.Indeterminate + panic(err) + } + + switch result := result.(type) { + case bool: + if !result { + policyEffects[i] = effect.Indeterminate + continue + } + case float64: + if result == 0 { + policyEffects[i] = effect.Indeterminate + continue + } else { + matcherResults[i] = result + } + default: + panic(errors.New("matcher result should be bool, int or float")) + } + + if eft, ok := parameters["p_eft"]; ok { + if eft == "allow" { + policyEffects[i] = effect.Allow + } else if eft == "deny" { + policyEffects[i] = effect.Deny + } else { + policyEffects[i] = effect.Indeterminate + } + } else { + policyEffects[i] = effect.Allow + } + + if e.model["e"]["e"].Value == "priority(p_eft) || deny" { + break + } + + } + } else { + policyEffects = make([]effect.Effect, 1) + matcherResults = make([]float64, 1) + + parameters := make(map[string]interface{}, 8) + for j, token := range e.model["r"]["r"].Tokens { + parameters[token] = rvals[j] + } + for _, token := range e.model["p"]["p"].Tokens { + parameters[token] = "" + } + + result, err := expression.Evaluate(parameters) + // log.LogPrint("Result: ", result) + + if err != nil { + policyEffects[0] = effect.Indeterminate + panic(err) + } + + if result.(bool) { + policyEffects[0] = effect.Allow + } else { + policyEffects[0] = effect.Indeterminate + } + } + + // log.LogPrint("Rule Results: ", policyEffects) + + result, err := e.eft.MergeEffects(e.model["e"]["e"].Value, policyEffects, matcherResults) + if err != nil { + panic(err) + } + + // Log request. + if log.GetLogger().IsEnabled() { + reqStr := "Request: " + for i, rval := range rvals { + if i != len(rvals)-1 { + reqStr += fmt.Sprintf("%v, ", rval) + } else { + reqStr += fmt.Sprintf("%v", rval) + } + } + reqStr += fmt.Sprintf(" ---> %t", result) + log.LogPrint(reqStr) + } + + return result +} diff --git a/vendor/github.com/casbin/casbin/enforcer_cached.go b/vendor/github.com/casbin/casbin/enforcer_cached.go new file mode 100644 index 000000000..fffc83403 --- /dev/null +++ b/vendor/github.com/casbin/casbin/enforcer_cached.go @@ -0,0 +1,74 @@ +// Copyright 2018 The casbin Authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package casbin + +import ( + "sync" +) + +// CachedEnforcer wraps Enforcer and provides decision cache +type CachedEnforcer struct { + *Enforcer + m map[string]bool + enableCache bool + locker *sync.Mutex +} + +// NewCachedEnforcer creates a cached enforcer via file or DB. +func NewCachedEnforcer(params ...interface{}) *CachedEnforcer { + e := &CachedEnforcer{} + e.Enforcer = NewEnforcer(params...) + e.enableCache = true + e.m = make(map[string]bool) + e.locker = new(sync.Mutex) + return e +} + +// EnableCache determines whether to enable cache on Enforce(). When enableCache is enabled, cached result (true | false) will be returned for previous decisions. +func (e *CachedEnforcer) EnableCache(enableCache bool) { + e.enableCache = enableCache +} + +// Enforce decides whether a "subject" can access a "object" with the operation "action", input parameters are usually: (sub, obj, act). +// if rvals is not string , ingore the cache +func (e *CachedEnforcer) Enforce(rvals ...interface{}) bool { + if !e.enableCache { + return e.Enforcer.Enforce(rvals...) + } + + key := "" + for _, rval := range rvals { + if val, ok := rval.(string); ok { + key += val + "$$" + } else { + return e.Enforcer.Enforce(rvals...) + } + } + + e.locker.Lock() + defer e.locker.Unlock() + if _, ok := e.m[key]; ok { + return e.m[key] + } else { + res := e.Enforcer.Enforce(rvals...) + e.m[key] = res + return res + } +} + +// InvalidateCache deletes all the existing cached decisions. +func (e *CachedEnforcer) InvalidateCache() { + e.m = make(map[string]bool) +} diff --git a/vendor/github.com/casbin/casbin/enforcer_safe.go b/vendor/github.com/casbin/casbin/enforcer_safe.go new file mode 100644 index 000000000..1cb358fee --- /dev/null +++ b/vendor/github.com/casbin/casbin/enforcer_safe.go @@ -0,0 +1,102 @@ +// Copyright 2017 The casbin Authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package casbin + +import ( + "fmt" +) + +// NewEnforcerSafe calls NewEnforcer in a safe way, returns error instead of causing panic. +func NewEnforcerSafe(params ...interface{}) (e *Enforcer, err error) { + defer func() { + if r := recover(); r != nil { + err = fmt.Errorf("%v", r) + e = nil + } + }() + + e = NewEnforcer(params...) + err = nil + return +} + +// LoadModelSafe calls LoadModel in a safe way, returns error instead of causing panic. +func (e *Enforcer) LoadModelSafe() (err error) { + defer func() { + if r := recover(); r != nil { + err = fmt.Errorf("%v", r) + } + }() + + e.LoadModel() + err = nil + return +} + +// EnforceSafe calls Enforce in a safe way, returns error instead of causing panic. +func (e *Enforcer) EnforceSafe(rvals ...interface{}) (result bool, err error) { + defer func() { + if r := recover(); r != nil { + err = fmt.Errorf("%v", r) + result = false + } + }() + + result = e.Enforce(rvals...) + err = nil + return +} + +// AddPolicySafe calls AddPolicy in a safe way, returns error instead of causing panic. +func (e *Enforcer) AddPolicySafe(params ...interface{}) (result bool, err error) { + defer func() { + if r := recover(); r != nil { + err = fmt.Errorf("%v", r) + result = false + } + }() + + result = e.AddNamedPolicy("p", params...) + err = nil + return +} + +// RemovePolicySafe calls RemovePolicy in a safe way, returns error instead of causing panic. +func (e *Enforcer) RemovePolicySafe(params ...interface{}) (result bool, err error) { + defer func() { + if r := recover(); r != nil { + err = fmt.Errorf("%v", r) + result = false + } + }() + + result = e.RemoveNamedPolicy("p", params...) + err = nil + return +} + +// RemoveFilteredPolicySafe calls RemoveFilteredPolicy in a safe way, returns error instead of causing panic. +func (e *Enforcer) RemoveFilteredPolicySafe(fieldIndex int, fieldValues ...string) (result bool, err error) { + defer func() { + if r := recover(); r != nil { + err = fmt.Errorf("%v", r) + result = false + } + }() + + result = e.RemoveFilteredNamedPolicy("p", fieldIndex, fieldValues...) + err = nil + return +} diff --git a/vendor/github.com/casbin/casbin/enforcer_synced.go b/vendor/github.com/casbin/casbin/enforcer_synced.go new file mode 100644 index 000000000..f752c71fc --- /dev/null +++ b/vendor/github.com/casbin/casbin/enforcer_synced.go @@ -0,0 +1,223 @@ +// Copyright 2017 The casbin Authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package casbin + +import ( + "log" + "sync" + "time" + + "github.com/casbin/casbin/persist" +) + +// SyncedEnforcer wraps Enforcer and provides synchronized access +type SyncedEnforcer struct { + *Enforcer + m sync.RWMutex + autoLoad bool +} + +// NewSyncedEnforcer creates a synchronized enforcer via file or DB. +func NewSyncedEnforcer(params ...interface{}) *SyncedEnforcer { + e := &SyncedEnforcer{} + e.Enforcer = NewEnforcer(params...) + e.autoLoad = false + return e +} + +// StartAutoLoadPolicy starts a go routine that will every specified duration call LoadPolicy +func (e *SyncedEnforcer) StartAutoLoadPolicy(d time.Duration) { + e.autoLoad = true + go func() { + n := 1 + log.Print("Start automatically load policy") + for { + if !e.autoLoad { + log.Print("Stop automatically load policy") + break + } + + // error intentionally ignored + e.LoadPolicy() + // Uncomment this line to see when the policy is loaded. + // log.Print("Load policy for time: ", n) + n++ + time.Sleep(d) + } + }() +} + +// StopAutoLoadPolicy causes the go routine to exit. +func (e *SyncedEnforcer) StopAutoLoadPolicy() { + e.autoLoad = false +} + +// SetWatcher sets the current watcher. +func (e *SyncedEnforcer) SetWatcher(watcher persist.Watcher) { + e.watcher = watcher + // error intentionally ignored + watcher.SetUpdateCallback(func(string) { e.LoadPolicy() }) +} + +// ClearPolicy clears all policy. +func (e *SyncedEnforcer) ClearPolicy() { + e.m.Lock() + defer e.m.Unlock() + e.Enforcer.ClearPolicy() +} + +// LoadPolicy reloads the policy from file/database. +func (e *SyncedEnforcer) LoadPolicy() error { + e.m.Lock() + defer e.m.Unlock() + return e.Enforcer.LoadPolicy() +} + +// SavePolicy saves the current policy (usually after changed with Casbin API) back to file/database. +func (e *SyncedEnforcer) SavePolicy() error { + e.m.RLock() + defer e.m.RUnlock() + return e.Enforcer.SavePolicy() +} + +// BuildRoleLinks manually rebuild the role inheritance relations. +func (e *SyncedEnforcer) BuildRoleLinks() { + e.m.RLock() + defer e.m.RUnlock() + e.Enforcer.BuildRoleLinks() +} + +// Enforce decides whether a "subject" can access a "object" with the operation "action", input parameters are usually: (sub, obj, act). +func (e *SyncedEnforcer) Enforce(rvals ...interface{}) bool { + e.m.RLock() + defer e.m.RUnlock() + return e.Enforcer.Enforce(rvals...) +} + +// GetAllSubjects gets the list of subjects that show up in the current policy. +func (e *SyncedEnforcer) GetAllSubjects() []string { + e.m.RLock() + defer e.m.RUnlock() + return e.Enforcer.GetAllSubjects() +} + +// GetAllObjects gets the list of objects that show up in the current policy. +func (e *SyncedEnforcer) GetAllObjects() []string { + e.m.RLock() + defer e.m.RUnlock() + return e.Enforcer.GetAllObjects() +} + +// GetAllActions gets the list of actions that show up in the current policy. +func (e *SyncedEnforcer) GetAllActions() []string { + e.m.RLock() + defer e.m.RUnlock() + return e.Enforcer.GetAllActions() +} + +// GetAllRoles gets the list of roles that show up in the current policy. +func (e *SyncedEnforcer) GetAllRoles() []string { + e.m.RLock() + defer e.m.RUnlock() + return e.Enforcer.GetAllRoles() +} + +// GetPolicy gets all the authorization rules in the policy. +func (e *SyncedEnforcer) GetPolicy() [][]string { + e.m.RLock() + defer e.m.RUnlock() + return e.Enforcer.GetPolicy() +} + +// GetFilteredPolicy gets all the authorization rules in the policy, field filters can be specified. +func (e *SyncedEnforcer) GetFilteredPolicy(fieldIndex int, fieldValues ...string) [][]string { + e.m.RLock() + defer e.m.RUnlock() + return e.Enforcer.GetFilteredPolicy(fieldIndex, fieldValues...) +} + +// GetGroupingPolicy gets all the role inheritance rules in the policy. +func (e *SyncedEnforcer) GetGroupingPolicy() [][]string { + e.m.RLock() + defer e.m.RUnlock() + return e.Enforcer.GetGroupingPolicy() +} + +// GetFilteredGroupingPolicy gets all the role inheritance rules in the policy, field filters can be specified. +func (e *SyncedEnforcer) GetFilteredGroupingPolicy(fieldIndex int, fieldValues ...string) [][]string { + e.m.RLock() + defer e.m.RUnlock() + return e.Enforcer.GetFilteredGroupingPolicy(fieldIndex, fieldValues...) +} + +// HasPolicy determines whether an authorization rule exists. +func (e *SyncedEnforcer) HasPolicy(params ...interface{}) bool { + e.m.RLock() + defer e.m.RUnlock() + return e.Enforcer.HasPolicy(params...) +} + +// AddPolicy adds an authorization rule to the current policy. +// If the rule already exists, the function returns false and the rule will not be added. +// Otherwise the function returns true by adding the new rule. +func (e *SyncedEnforcer) AddPolicy(params ...interface{}) bool { + e.m.Lock() + defer e.m.Unlock() + return e.Enforcer.AddPolicy(params...) +} + +// RemovePolicy removes an authorization rule from the current policy. +func (e *SyncedEnforcer) RemovePolicy(params ...interface{}) bool { + e.m.Lock() + defer e.m.Unlock() + return e.Enforcer.RemovePolicy(params...) +} + +// RemoveFilteredPolicy removes an authorization rule from the current policy, field filters can be specified. +func (e *SyncedEnforcer) RemoveFilteredPolicy(fieldIndex int, fieldValues ...string) bool { + e.m.Lock() + defer e.m.Unlock() + return e.Enforcer.RemoveFilteredPolicy(fieldIndex, fieldValues...) +} + +// HasGroupingPolicy determines whether a role inheritance rule exists. +func (e *SyncedEnforcer) HasGroupingPolicy(params ...interface{}) bool { + e.m.RLock() + defer e.m.RUnlock() + return e.Enforcer.HasGroupingPolicy(params...) +} + +// AddGroupingPolicy adds a role inheritance rule to the current policy. +// If the rule already exists, the function returns false and the rule will not be added. +// Otherwise the function returns true by adding the new rule. +func (e *SyncedEnforcer) AddGroupingPolicy(params ...interface{}) bool { + e.m.Lock() + defer e.m.Unlock() + return e.Enforcer.AddGroupingPolicy(params...) +} + +// RemoveGroupingPolicy removes a role inheritance rule from the current policy. +func (e *SyncedEnforcer) RemoveGroupingPolicy(params ...interface{}) bool { + e.m.Lock() + defer e.m.Unlock() + return e.Enforcer.RemoveGroupingPolicy(params...) +} + +// RemoveFilteredGroupingPolicy removes a role inheritance rule from the current policy, field filters can be specified. +func (e *SyncedEnforcer) RemoveFilteredGroupingPolicy(fieldIndex int, fieldValues ...string) bool { + e.m.Lock() + defer e.m.Unlock() + return e.Enforcer.RemoveFilteredGroupingPolicy(fieldIndex, fieldValues...) +} diff --git a/vendor/github.com/casbin/casbin/internal_api.go b/vendor/github.com/casbin/casbin/internal_api.go new file mode 100644 index 000000000..6e6acead9 --- /dev/null +++ b/vendor/github.com/casbin/casbin/internal_api.go @@ -0,0 +1,85 @@ +// Copyright 2017 The casbin Authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package casbin + +const ( + notImplemented = "not implemented" +) + +// addPolicy adds a rule to the current policy. +func (e *Enforcer) addPolicy(sec string, ptype string, rule []string) bool { + ruleAdded := e.model.AddPolicy(sec, ptype, rule) + if !ruleAdded { + return ruleAdded + } + + if e.adapter != nil && e.autoSave { + if err := e.adapter.AddPolicy(sec, ptype, rule); err != nil { + if err.Error() != notImplemented { + panic(err) + } + } + if e.watcher != nil { + // error intentionally ignored + e.watcher.Update() + } + } + + return ruleAdded +} + +// removePolicy removes a rule from the current policy. +func (e *Enforcer) removePolicy(sec string, ptype string, rule []string) bool { + ruleRemoved := e.model.RemovePolicy(sec, ptype, rule) + if !ruleRemoved { + return ruleRemoved + } + + if e.adapter != nil && e.autoSave { + if err := e.adapter.RemovePolicy(sec, ptype, rule); err != nil { + if err.Error() != notImplemented { + panic(err) + } + } + if e.watcher != nil { + // error intentionally ignored + e.watcher.Update() + } + } + + return ruleRemoved +} + +// removeFilteredPolicy removes rules based on field filters from the current policy. +func (e *Enforcer) removeFilteredPolicy(sec string, ptype string, fieldIndex int, fieldValues ...string) bool { + ruleRemoved := e.model.RemoveFilteredPolicy(sec, ptype, fieldIndex, fieldValues...) + if !ruleRemoved { + return ruleRemoved + } + + if e.adapter != nil && e.autoSave { + if err := e.adapter.RemoveFilteredPolicy(sec, ptype, fieldIndex, fieldValues...); err != nil { + if err.Error() != notImplemented { + panic(err) + } + } + if e.watcher != nil { + // error intentionally ignored + e.watcher.Update() + } + } + + return ruleRemoved +} diff --git a/vendor/github.com/casbin/casbin/log/default_logger.go b/vendor/github.com/casbin/casbin/log/default_logger.go new file mode 100644 index 000000000..74a042281 --- /dev/null +++ b/vendor/github.com/casbin/casbin/log/default_logger.go @@ -0,0 +1,42 @@ +// Copyright 2018 The casbin Authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package log + +import "log" + +// DefaultLogger is the implementation for a Logger using golang log. +type DefaultLogger struct { + enable bool +} + +func (l *DefaultLogger) EnableLog(enable bool) { + l.enable = enable +} + +func (l *DefaultLogger) IsEnabled() bool { + return l.enable +} + +func (l *DefaultLogger) Print(v ...interface{}) { + if l.enable { + log.Print(v...) + } +} + +func (l *DefaultLogger) Printf(format string, v ...interface{}) { + if l.enable { + log.Printf(format, v...) + } +} diff --git a/vendor/github.com/casbin/casbin/log/log_util.go b/vendor/github.com/casbin/casbin/log/log_util.go new file mode 100644 index 000000000..ef4b8165a --- /dev/null +++ b/vendor/github.com/casbin/casbin/log/log_util.go @@ -0,0 +1,37 @@ +// Copyright 2017 The casbin Authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package log + +var logger Logger = &DefaultLogger{} + +// SetLogger sets the current logger. +func SetLogger(l Logger) { + logger = l +} + +// GetLogger returns the current logger. +func GetLogger() Logger { + return logger +} + +// LogPrint prints the log. +func LogPrint(v ...interface{}) { + logger.Print(v...) +} + +// LogPrintf prints the log with the format. +func LogPrintf(format string, v ...interface{}) { + logger.Printf(format, v...) +} diff --git a/vendor/github.com/casbin/casbin/log/logger.go b/vendor/github.com/casbin/casbin/log/logger.go new file mode 100644 index 000000000..e3aadd1a5 --- /dev/null +++ b/vendor/github.com/casbin/casbin/log/logger.go @@ -0,0 +1,30 @@ +// Copyright 2018 The casbin Authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package log + +// Logger is the logging interface implementation. +type Logger interface { + //EnableLog controls whether print the message. + EnableLog(bool) + + //IsEnabled returns if logger is enabled. + IsEnabled() bool + + //Print formats using the default formats for its operands and logs the message. + Print(...interface{}) + + //Printf formats according to a format specifier and logs the message. + Printf(string, ...interface{}) +} diff --git a/vendor/github.com/casbin/casbin/management_api.go b/vendor/github.com/casbin/casbin/management_api.go new file mode 100644 index 000000000..355e17a71 --- /dev/null +++ b/vendor/github.com/casbin/casbin/management_api.go @@ -0,0 +1,265 @@ +// Copyright 2017 The casbin Authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package casbin + +// GetAllSubjects gets the list of subjects that show up in the current policy. +func (e *Enforcer) GetAllSubjects() []string { + return e.GetAllNamedSubjects("p") +} + +// GetAllNamedSubjects gets the list of subjects that show up in the current named policy. +func (e *Enforcer) GetAllNamedSubjects(ptype string) []string { + return e.model.GetValuesForFieldInPolicy("p", ptype, 0) +} + +// GetAllObjects gets the list of objects that show up in the current policy. +func (e *Enforcer) GetAllObjects() []string { + return e.GetAllNamedObjects("p") +} + +// GetAllNamedObjects gets the list of objects that show up in the current named policy. +func (e *Enforcer) GetAllNamedObjects(ptype string) []string { + return e.model.GetValuesForFieldInPolicy("p", ptype, 1) +} + +// GetAllActions gets the list of actions that show up in the current policy. +func (e *Enforcer) GetAllActions() []string { + return e.GetAllNamedActions("p") +} + +// GetAllNamedActions gets the list of actions that show up in the current named policy. +func (e *Enforcer) GetAllNamedActions(ptype string) []string { + return e.model.GetValuesForFieldInPolicy("p", ptype, 2) +} + +// GetAllRoles gets the list of roles that show up in the current policy. +func (e *Enforcer) GetAllRoles() []string { + return e.GetAllNamedRoles("g") +} + +// GetAllNamedRoles gets the list of roles that show up in the current named policy. +func (e *Enforcer) GetAllNamedRoles(ptype string) []string { + return e.model.GetValuesForFieldInPolicy("g", ptype, 1) +} + +// GetPolicy gets all the authorization rules in the policy. +func (e *Enforcer) GetPolicy() [][]string { + return e.GetNamedPolicy("p") +} + +// GetFilteredPolicy gets all the authorization rules in the policy, field filters can be specified. +func (e *Enforcer) GetFilteredPolicy(fieldIndex int, fieldValues ...string) [][]string { + return e.GetFilteredNamedPolicy("p", fieldIndex, fieldValues...) +} + +// GetNamedPolicy gets all the authorization rules in the named policy. +func (e *Enforcer) GetNamedPolicy(ptype string) [][]string { + return e.model.GetPolicy("p", ptype) +} + +// GetFilteredNamedPolicy gets all the authorization rules in the named policy, field filters can be specified. +func (e *Enforcer) GetFilteredNamedPolicy(ptype string, fieldIndex int, fieldValues ...string) [][]string { + return e.model.GetFilteredPolicy("p", ptype, fieldIndex, fieldValues...) +} + +// GetGroupingPolicy gets all the role inheritance rules in the policy. +func (e *Enforcer) GetGroupingPolicy() [][]string { + return e.GetNamedGroupingPolicy("g") +} + +// GetFilteredGroupingPolicy gets all the role inheritance rules in the policy, field filters can be specified. +func (e *Enforcer) GetFilteredGroupingPolicy(fieldIndex int, fieldValues ...string) [][]string { + return e.GetFilteredNamedGroupingPolicy("g", fieldIndex, fieldValues...) +} + +// GetNamedGroupingPolicy gets all the role inheritance rules in the policy. +func (e *Enforcer) GetNamedGroupingPolicy(ptype string) [][]string { + return e.model.GetPolicy("g", ptype) +} + +// GetFilteredNamedGroupingPolicy gets all the role inheritance rules in the policy, field filters can be specified. +func (e *Enforcer) GetFilteredNamedGroupingPolicy(ptype string, fieldIndex int, fieldValues ...string) [][]string { + return e.model.GetFilteredPolicy("g", ptype, fieldIndex, fieldValues...) +} + +// HasPolicy determines whether an authorization rule exists. +func (e *Enforcer) HasPolicy(params ...interface{}) bool { + return e.HasNamedPolicy("p", params...) +} + +// HasNamedPolicy determines whether a named authorization rule exists. +func (e *Enforcer) HasNamedPolicy(ptype string, params ...interface{}) bool { + if strSlice, ok := params[0].([]string); len(params) == 1 && ok { + return e.model.HasPolicy("p", ptype, strSlice) + } + + policy := make([]string, 0) + for _, param := range params { + policy = append(policy, param.(string)) + } + + return e.model.HasPolicy("p", ptype, policy) +} + +// AddPolicy adds an authorization rule to the current policy. +// If the rule already exists, the function returns false and the rule will not be added. +// Otherwise the function returns true by adding the new rule. +func (e *Enforcer) AddPolicy(params ...interface{}) bool { + return e.AddNamedPolicy("p", params...) +} + +// AddNamedPolicy adds an authorization rule to the current named policy. +// If the rule already exists, the function returns false and the rule will not be added. +// Otherwise the function returns true by adding the new rule. +func (e *Enforcer) AddNamedPolicy(ptype string, params ...interface{}) bool { + var ruleAdded bool + if strSlice, ok := params[0].([]string); len(params) == 1 && ok { + ruleAdded = e.addPolicy("p", ptype, strSlice) + } else { + policy := make([]string, 0) + for _, param := range params { + policy = append(policy, param.(string)) + } + + ruleAdded = e.addPolicy("p", ptype, policy) + } + + return ruleAdded +} + +// RemovePolicy removes an authorization rule from the current policy. +func (e *Enforcer) RemovePolicy(params ...interface{}) bool { + return e.RemoveNamedPolicy("p", params...) +} + +// RemoveFilteredPolicy removes an authorization rule from the current policy, field filters can be specified. +func (e *Enforcer) RemoveFilteredPolicy(fieldIndex int, fieldValues ...string) bool { + return e.RemoveFilteredNamedPolicy("p", fieldIndex, fieldValues...) +} + +// RemoveNamedPolicy removes an authorization rule from the current named policy. +func (e *Enforcer) RemoveNamedPolicy(ptype string, params ...interface{}) bool { + var ruleRemoved bool + if strSlice, ok := params[0].([]string); len(params) == 1 && ok { + ruleRemoved = e.removePolicy("p", ptype, strSlice) + } else { + policy := make([]string, 0) + for _, param := range params { + policy = append(policy, param.(string)) + } + + ruleRemoved = e.removePolicy("p", ptype, policy) + } + + return ruleRemoved +} + +// RemoveFilteredNamedPolicy removes an authorization rule from the current named policy, field filters can be specified. +func (e *Enforcer) RemoveFilteredNamedPolicy(ptype string, fieldIndex int, fieldValues ...string) bool { + return e.removeFilteredPolicy("p", ptype, fieldIndex, fieldValues...) +} + +// HasGroupingPolicy determines whether a role inheritance rule exists. +func (e *Enforcer) HasGroupingPolicy(params ...interface{}) bool { + return e.HasNamedGroupingPolicy("g", params...) +} + +// HasNamedGroupingPolicy determines whether a named role inheritance rule exists. +func (e *Enforcer) HasNamedGroupingPolicy(ptype string, params ...interface{}) bool { + if strSlice, ok := params[0].([]string); len(params) == 1 && ok { + return e.model.HasPolicy("g", ptype, strSlice) + } + + policy := make([]string, 0) + for _, param := range params { + policy = append(policy, param.(string)) + } + + return e.model.HasPolicy("g", ptype, policy) +} + +// AddGroupingPolicy adds a role inheritance rule to the current policy. +// If the rule already exists, the function returns false and the rule will not be added. +// Otherwise the function returns true by adding the new rule. +func (e *Enforcer) AddGroupingPolicy(params ...interface{}) bool { + return e.AddNamedGroupingPolicy("g", params...) +} + +// AddNamedGroupingPolicy adds a named role inheritance rule to the current policy. +// If the rule already exists, the function returns false and the rule will not be added. +// Otherwise the function returns true by adding the new rule. +func (e *Enforcer) AddNamedGroupingPolicy(ptype string, params ...interface{}) bool { + var ruleAdded bool + if strSlice, ok := params[0].([]string); len(params) == 1 && ok { + ruleAdded = e.addPolicy("g", ptype, strSlice) + } else { + policy := make([]string, 0) + for _, param := range params { + policy = append(policy, param.(string)) + } + + ruleAdded = e.addPolicy("g", ptype, policy) + } + + if e.autoBuildRoleLinks { + e.BuildRoleLinks() + } + return ruleAdded +} + +// RemoveGroupingPolicy removes a role inheritance rule from the current policy. +func (e *Enforcer) RemoveGroupingPolicy(params ...interface{}) bool { + return e.RemoveNamedGroupingPolicy("g", params...) +} + +// RemoveFilteredGroupingPolicy removes a role inheritance rule from the current policy, field filters can be specified. +func (e *Enforcer) RemoveFilteredGroupingPolicy(fieldIndex int, fieldValues ...string) bool { + return e.RemoveFilteredNamedGroupingPolicy("g", fieldIndex, fieldValues...) +} + +// RemoveNamedGroupingPolicy removes a role inheritance rule from the current named policy. +func (e *Enforcer) RemoveNamedGroupingPolicy(ptype string, params ...interface{}) bool { + var ruleRemoved bool + if strSlice, ok := params[0].([]string); len(params) == 1 && ok { + ruleRemoved = e.removePolicy("g", ptype, strSlice) + } else { + policy := make([]string, 0) + for _, param := range params { + policy = append(policy, param.(string)) + } + + ruleRemoved = e.removePolicy("g", ptype, policy) + } + + if e.autoBuildRoleLinks { + e.BuildRoleLinks() + } + return ruleRemoved +} + +// RemoveFilteredNamedGroupingPolicy removes a role inheritance rule from the current named policy, field filters can be specified. +func (e *Enforcer) RemoveFilteredNamedGroupingPolicy(ptype string, fieldIndex int, fieldValues ...string) bool { + ruleRemoved := e.removeFilteredPolicy("g", ptype, fieldIndex, fieldValues...) + + if e.autoBuildRoleLinks { + e.BuildRoleLinks() + } + return ruleRemoved +} + +// AddFunction adds a customized function. +func (e *Enforcer) AddFunction(name string, function func(args ...interface{}) (interface{}, error)) { + e.fm.AddFunction(name, function) +} diff --git a/vendor/github.com/casbin/casbin/model/assertion.go b/vendor/github.com/casbin/casbin/model/assertion.go new file mode 100644 index 000000000..0042ab1a4 --- /dev/null +++ b/vendor/github.com/casbin/casbin/model/assertion.go @@ -0,0 +1,60 @@ +// Copyright 2017 The casbin Authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package model + +import ( + "errors" + "strings" + + "github.com/casbin/casbin/log" + "github.com/casbin/casbin/rbac" +) + +// Assertion represents an expression in a section of the model. +// For example: r = sub, obj, act +type Assertion struct { + Key string + Value string + Tokens []string + Policy [][]string + RM rbac.RoleManager +} + +func (ast *Assertion) buildRoleLinks(rm rbac.RoleManager) { + ast.RM = rm + count := strings.Count(ast.Value, "_") + for _, rule := range ast.Policy { + if count < 2 { + panic(errors.New("the number of \"_\" in role definition should be at least 2")) + } + if len(rule) < count { + panic(errors.New("grouping policy elements do not meet role definition")) + } + + if count == 2 { + // error intentionally ignored + ast.RM.AddLink(rule[0], rule[1]) + } else if count == 3 { + // error intentionally ignored + ast.RM.AddLink(rule[0], rule[1], rule[2]) + } else if count == 4 { + // error intentionally ignored + ast.RM.AddLink(rule[0], rule[1], rule[2], rule[3]) + } + } + + log.LogPrint("Role links for: " + ast.Key) + ast.RM.PrintRoles() +} diff --git a/vendor/github.com/casbin/casbin/model/function.go b/vendor/github.com/casbin/casbin/model/function.go new file mode 100644 index 000000000..5c81d1801 --- /dev/null +++ b/vendor/github.com/casbin/casbin/model/function.go @@ -0,0 +1,40 @@ +// Copyright 2017 The casbin Authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package model + +import "github.com/casbin/casbin/util" + +// FunctionMap represents the collection of Function. +type FunctionMap map[string]func(args ...interface{}) (interface{}, error) + +// Function represents a function that is used in the matchers, used to get attributes in ABAC. +type Function func(args ...interface{}) (interface{}, error) + +// AddFunction adds an expression function. +func (fm FunctionMap) AddFunction(name string, function Function) { + fm[name] = function +} + +// LoadFunctionMap loads an initial function map. +func LoadFunctionMap() FunctionMap { + fm := make(FunctionMap) + + fm.AddFunction("keyMatch", util.KeyMatchFunc) + fm.AddFunction("keyMatch2", util.KeyMatch2Func) + fm.AddFunction("regexMatch", util.RegexMatchFunc) + fm.AddFunction("ipMatch", util.IPMatchFunc) + + return fm +} diff --git a/vendor/github.com/casbin/casbin/model/model.go b/vendor/github.com/casbin/casbin/model/model.go new file mode 100644 index 000000000..91de12793 --- /dev/null +++ b/vendor/github.com/casbin/casbin/model/model.go @@ -0,0 +1,130 @@ +// Copyright 2017 The casbin Authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package model + +import ( + "strconv" + "strings" + + "github.com/casbin/casbin/config" + "github.com/casbin/casbin/log" + "github.com/casbin/casbin/util" +) + +// Model represents the whole access control model. +type Model map[string]AssertionMap + +// AssertionMap is the collection of assertions, can be "r", "p", "g", "e", "m". +type AssertionMap map[string]*Assertion + +var sectionNameMap = map[string]string{ + "r": "request_definition", + "p": "policy_definition", + "g": "role_definition", + "e": "policy_effect", + "m": "matchers", +} + +func loadAssertion(model Model, cfg config.ConfigInterface, sec string, key string) bool { + value := cfg.String(sectionNameMap[sec] + "::" + key) + return model.AddDef(sec, key, value) +} + +// AddDef adds an assertion to the model. +func (model Model) AddDef(sec string, key string, value string) bool { + ast := Assertion{} + ast.Key = key + ast.Value = value + + if ast.Value == "" { + return false + } + + if sec == "r" || sec == "p" { + ast.Tokens = strings.Split(ast.Value, ", ") + for i := range ast.Tokens { + ast.Tokens[i] = key + "_" + ast.Tokens[i] + } + } else { + ast.Value = util.RemoveComments(util.EscapeAssertion(ast.Value)) + } + + _, ok := model[sec] + if !ok { + model[sec] = make(AssertionMap) + } + + model[sec][key] = &ast + return true +} + +func getKeySuffix(i int) string { + if i == 1 { + return "" + } + + return strconv.Itoa(i) +} + +func loadSection(model Model, cfg config.ConfigInterface, sec string) { + i := 1 + for { + if !loadAssertion(model, cfg, sec, sec+getKeySuffix(i)) { + break + } else { + i++ + } + } +} + +// LoadModel loads the model from model CONF file. +func (model Model) LoadModel(path string) { + cfg, err := config.NewConfig(path) + if err != nil { + panic(err) + } + + loadSection(model, cfg, "r") + loadSection(model, cfg, "p") + loadSection(model, cfg, "e") + loadSection(model, cfg, "m") + + loadSection(model, cfg, "g") +} + +// LoadModelFromText loads the model from the text. +func (model Model) LoadModelFromText(text string) { + cfg, err := config.NewConfigFromText(text) + if err != nil { + panic(err) + } + + loadSection(model, cfg, "r") + loadSection(model, cfg, "p") + loadSection(model, cfg, "e") + loadSection(model, cfg, "m") + + loadSection(model, cfg, "g") +} + +// PrintModel prints the model to the log. +func (model Model) PrintModel() { + log.LogPrint("Model:") + for k, v := range model { + for i, j := range v { + log.LogPrintf("%s.%s: %s", k, i, j.Value) + } + } +} diff --git a/vendor/github.com/casbin/casbin/model/policy.go b/vendor/github.com/casbin/casbin/model/policy.go new file mode 100644 index 000000000..fd5641830 --- /dev/null +++ b/vendor/github.com/casbin/casbin/model/policy.go @@ -0,0 +1,146 @@ +// Copyright 2017 The casbin Authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package model + +import ( + "github.com/casbin/casbin/log" + "github.com/casbin/casbin/rbac" + "github.com/casbin/casbin/util" +) + +// BuildRoleLinks initializes the roles in RBAC. +func (model Model) BuildRoleLinks(rm rbac.RoleManager) { + for _, ast := range model["g"] { + ast.buildRoleLinks(rm) + } +} + +// PrintPolicy prints the policy to log. +func (model Model) PrintPolicy() { + log.LogPrint("Policy:") + for key, ast := range model["p"] { + log.LogPrint(key, ": ", ast.Value, ": ", ast.Policy) + } + + for key, ast := range model["g"] { + log.LogPrint(key, ": ", ast.Value, ": ", ast.Policy) + } +} + +// ClearPolicy clears all current policy. +func (model Model) ClearPolicy() { + for _, ast := range model["p"] { + ast.Policy = nil + } + + for _, ast := range model["g"] { + ast.Policy = nil + } +} + +// GetPolicy gets all rules in a policy. +func (model Model) GetPolicy(sec string, ptype string) [][]string { + return model[sec][ptype].Policy +} + +// GetFilteredPolicy gets rules based on field filters from a policy. +func (model Model) GetFilteredPolicy(sec string, ptype string, fieldIndex int, fieldValues ...string) [][]string { + res := [][]string{} + + for _, rule := range model[sec][ptype].Policy { + matched := true + for i, fieldValue := range fieldValues { + if fieldValue != "" && rule[fieldIndex+i] != fieldValue { + matched = false + break + } + } + + if matched { + res = append(res, rule) + } + } + + return res +} + +// HasPolicy determines whether a model has the specified policy rule. +func (model Model) HasPolicy(sec string, ptype string, rule []string) bool { + for _, r := range model[sec][ptype].Policy { + if util.ArrayEquals(rule, r) { + return true + } + } + + return false +} + +// AddPolicy adds a policy rule to the model. +func (model Model) AddPolicy(sec string, ptype string, rule []string) bool { + if !model.HasPolicy(sec, ptype, rule) { + model[sec][ptype].Policy = append(model[sec][ptype].Policy, rule) + return true + } + return false +} + +// RemovePolicy removes a policy rule from the model. +func (model Model) RemovePolicy(sec string, ptype string, rule []string) bool { + for i, r := range model[sec][ptype].Policy { + if util.ArrayEquals(rule, r) { + model[sec][ptype].Policy = append(model[sec][ptype].Policy[:i], model[sec][ptype].Policy[i+1:]...) + return true + } + } + + return false +} + +// RemoveFilteredPolicy removes policy rules based on field filters from the model. +func (model Model) RemoveFilteredPolicy(sec string, ptype string, fieldIndex int, fieldValues ...string) bool { + tmp := [][]string{} + res := false + for _, rule := range model[sec][ptype].Policy { + matched := true + for i, fieldValue := range fieldValues { + if fieldValue != "" && rule[fieldIndex+i] != fieldValue { + matched = false + break + } + } + + if matched { + res = true + } else { + tmp = append(tmp, rule) + } + } + + model[sec][ptype].Policy = tmp + return res +} + +// GetValuesForFieldInPolicy gets all values for a field for all rules in a policy, duplicated values are removed. +func (model Model) GetValuesForFieldInPolicy(sec string, ptype string, fieldIndex int) []string { + values := []string{} + + for _, rule := range model[sec][ptype].Policy { + values = append(values, rule[fieldIndex]) + } + + util.ArrayRemoveDuplicates(&values) + + return values +} diff --git a/vendor/github.com/casbin/casbin/persist/adapter.go b/vendor/github.com/casbin/casbin/persist/adapter.go new file mode 100644 index 000000000..0d994b98f --- /dev/null +++ b/vendor/github.com/casbin/casbin/persist/adapter.go @@ -0,0 +1,56 @@ +// Copyright 2017 The casbin Authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package persist + +import ( + "strings" + + "github.com/casbin/casbin/model" +) + +// LoadPolicyLine loads a text line as a policy rule to model. +func LoadPolicyLine(line string, model model.Model) { + if line == "" { + return + } + + if strings.HasPrefix(line, "#") { + return + } + + tokens := strings.Split(line, ", ") + + key := tokens[0] + sec := key[:1] + model[sec][key].Policy = append(model[sec][key].Policy, tokens[1:]) +} + +// Adapter is the interface for Casbin adapters. +type Adapter interface { + // LoadPolicy loads all policy rules from the storage. + LoadPolicy(model model.Model) error + // SavePolicy saves all policy rules to the storage. + SavePolicy(model model.Model) error + + // AddPolicy adds a policy rule to the storage. + // This is part of the Auto-Save feature. + AddPolicy(sec string, ptype string, rule []string) error + // RemovePolicy removes a policy rule from the storage. + // This is part of the Auto-Save feature. + RemovePolicy(sec string, ptype string, rule []string) error + // RemoveFilteredPolicy removes policy rules that match the filter from the storage. + // This is part of the Auto-Save feature. + RemoveFilteredPolicy(sec string, ptype string, fieldIndex int, fieldValues ...string) error +} diff --git a/vendor/github.com/casbin/casbin/persist/adapter_filtered.go b/vendor/github.com/casbin/casbin/persist/adapter_filtered.go new file mode 100644 index 000000000..fab51e725 --- /dev/null +++ b/vendor/github.com/casbin/casbin/persist/adapter_filtered.go @@ -0,0 +1,29 @@ +// Copyright 2017 The casbin Authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package persist + +import ( + "github.com/casbin/casbin/model" +) + +// FilteredAdapter is the interface for Casbin adapters supporting filtered policies. +type FilteredAdapter interface { + Adapter + + // LoadFilteredPolicy loads only policy rules that match the filter. + LoadFilteredPolicy(model model.Model, filter interface{}) error + // IsFiltered returns true if the loaded policy has been filtered. + IsFiltered() bool +} diff --git a/vendor/github.com/casbin/casbin/persist/file-adapter/adapter.go b/vendor/github.com/casbin/casbin/persist/file-adapter/adapter.go new file mode 100644 index 000000000..91bf7a9ac --- /dev/null +++ b/vendor/github.com/casbin/casbin/persist/file-adapter/adapter.go @@ -0,0 +1,117 @@ +// Copyright 2017 The casbin Authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package fileadapter + +import ( + "bufio" + "bytes" + "errors" + "os" + "strings" + + "github.com/casbin/casbin/model" + "github.com/casbin/casbin/persist" + "github.com/casbin/casbin/util" +) + +// Adapter is the file adapter for Casbin. +// It can load policy from file or save policy to file. +type Adapter struct { + filePath string +} + +// NewAdapter is the constructor for Adapter. +func NewAdapter(filePath string) *Adapter { + return &Adapter{filePath: filePath} +} + +// LoadPolicy loads all policy rules from the storage. +func (a *Adapter) LoadPolicy(model model.Model) error { + if a.filePath == "" { + return errors.New("invalid file path, file path cannot be empty") + } + + return a.loadPolicyFile(model, persist.LoadPolicyLine) +} + +// SavePolicy saves all policy rules to the storage. +func (a *Adapter) SavePolicy(model model.Model) error { + if a.filePath == "" { + return errors.New("invalid file path, file path cannot be empty") + } + + var tmp bytes.Buffer + + for ptype, ast := range model["p"] { + for _, rule := range ast.Policy { + tmp.WriteString(ptype + ", ") + tmp.WriteString(util.ArrayToString(rule)) + tmp.WriteString("\n") + } + } + + for ptype, ast := range model["g"] { + for _, rule := range ast.Policy { + tmp.WriteString(ptype + ", ") + tmp.WriteString(util.ArrayToString(rule)) + tmp.WriteString("\n") + } + } + + return a.savePolicyFile(strings.TrimRight(tmp.String(), "\n")) +} + +func (a *Adapter) loadPolicyFile(model model.Model, handler func(string, model.Model)) error { + f, err := os.Open(a.filePath) + if err != nil { + return err + } + defer f.Close() + + scanner := bufio.NewScanner(f) + for scanner.Scan() { + line := strings.TrimSpace(scanner.Text()) + handler(line, model) + } + return scanner.Err() +} + +func (a *Adapter) savePolicyFile(text string) error { + f, err := os.Create(a.filePath) + if err != nil { + return err + } + w := bufio.NewWriter(f) + // error intentionally ignored + w.WriteString(text) + w.Flush() + f.Close() + return nil +} + +// AddPolicy adds a policy rule to the storage. +func (a *Adapter) AddPolicy(sec string, ptype string, rule []string) error { + return errors.New("not implemented") +} + +// RemovePolicy removes a policy rule from the storage. +func (a *Adapter) RemovePolicy(sec string, ptype string, rule []string) error { + return errors.New("not implemented") +} + +// RemoveFilteredPolicy removes policy rules that match the filter from the storage. +func (a *Adapter) RemoveFilteredPolicy(sec string, ptype string, fieldIndex int, fieldValues ...string) error { + return errors.New("not implemented") +} diff --git a/vendor/github.com/casbin/casbin/persist/file-adapter/adapter_filtered.go b/vendor/github.com/casbin/casbin/persist/file-adapter/adapter_filtered.go new file mode 100644 index 000000000..f0b18c3b0 --- /dev/null +++ b/vendor/github.com/casbin/casbin/persist/file-adapter/adapter_filtered.go @@ -0,0 +1,137 @@ +// Copyright 2017 The casbin Authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package fileadapter + +import ( + "bufio" + "errors" + "os" + "strings" + + "github.com/casbin/casbin/model" + "github.com/casbin/casbin/persist" +) + +// FilteredAdapter is the filtered file adapter for Casbin. It can load policy +// from file or save policy to file and supports loading of filtered policies. +type FilteredAdapter struct { + *Adapter + filtered bool +} + +// Filter defines the filtering rules for a FilteredAdapter's policy. Empty values +// are ignored, but all others must match the filter. +type Filter struct { + P []string + G []string +} + +// NewFilteredAdapter is the constructor for FilteredAdapter. +func NewFilteredAdapter(filePath string) *FilteredAdapter { + a := FilteredAdapter{} + a.Adapter = NewAdapter(filePath) + return &a +} + +// LoadPolicy loads all policy rules from the storage. +func (a *FilteredAdapter) LoadPolicy(model model.Model) error { + a.filtered = false + return a.Adapter.LoadPolicy(model) +} + +// LoadFilteredPolicy loads only policy rules that match the filter. +func (a *FilteredAdapter) LoadFilteredPolicy(model model.Model, filter interface{}) error { + if filter == nil { + return a.LoadPolicy(model) + } + if a.filePath == "" { + return errors.New("invalid file path, file path cannot be empty") + } + + filterValue, ok := filter.(*Filter) + if !ok { + return errors.New("invalid filter type") + } + err := a.loadFilteredPolicyFile(model, filterValue, persist.LoadPolicyLine) + if err == nil { + a.filtered = true + } + return err +} + +func (a *FilteredAdapter) loadFilteredPolicyFile(model model.Model, filter *Filter, handler func(string, model.Model)) error { + f, err := os.Open(a.filePath) + if err != nil { + return err + } + defer f.Close() + + scanner := bufio.NewScanner(f) + for scanner.Scan() { + line := strings.TrimSpace(scanner.Text()) + + if filterLine(line, filter) { + continue + } + + handler(line, model) + } + return scanner.Err() +} + +// IsFiltered returns true if the loaded policy has been filtered. +func (a *FilteredAdapter) IsFiltered() bool { + return a.filtered +} + +// SavePolicy saves all policy rules to the storage. +func (a *FilteredAdapter) SavePolicy(model model.Model) error { + if a.filtered { + return errors.New("cannot save a filtered policy") + } + return a.Adapter.SavePolicy(model) +} + +func filterLine(line string, filter *Filter) bool { + if filter == nil { + return false + } + p := strings.Split(line, ",") + if len(p) == 0 { + return true + } + var filterSlice []string + switch strings.TrimSpace(p[0]) { + case "p": + filterSlice = filter.P + case "g": + filterSlice = filter.G + } + return filterWords(p, filterSlice) +} + +func filterWords(line []string, filter []string) bool { + if len(line) < len(filter)+1 { + return true + } + var skipLine bool + for i, v := range filter { + if len(v) > 0 && strings.TrimSpace(v) != strings.TrimSpace(line[i+1]) { + skipLine = true + break + } + } + return skipLine +} diff --git a/vendor/github.com/casbin/casbin/persist/file-adapter/adapter_mock.go b/vendor/github.com/casbin/casbin/persist/file-adapter/adapter_mock.go new file mode 100644 index 000000000..0d7f83a77 --- /dev/null +++ b/vendor/github.com/casbin/casbin/persist/file-adapter/adapter_mock.go @@ -0,0 +1,100 @@ +// Copyright 2017 The casbin Authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package fileadapter + +import ( + "bufio" + "errors" + "io" + "os" + "strings" + + "github.com/casbin/casbin/model" + "github.com/casbin/casbin/persist" +) + +// AdapterMock is the file adapter for Casbin. +// It can load policy from file or save policy to file. +type AdapterMock struct { + filePath string + errorValue string +} + +// NewAdapterMock is the constructor for AdapterMock. +func NewAdapterMock(filePath string) *AdapterMock { + a := AdapterMock{} + a.filePath = filePath + return &a +} + +// LoadPolicy loads all policy rules from the storage. +func (a *AdapterMock) LoadPolicy(model model.Model) error { + err := a.loadPolicyFile(model, persist.LoadPolicyLine) + return err +} + +// SavePolicy saves all policy rules to the storage. +func (a *AdapterMock) SavePolicy(model model.Model) error { + return nil +} + +func (a *AdapterMock) loadPolicyFile(model model.Model, handler func(string, model.Model)) error { + f, err := os.Open(a.filePath) + if err != nil { + return err + } + defer f.Close() + + buf := bufio.NewReader(f) + for { + line, err := buf.ReadString('\n') + line = strings.TrimSpace(line) + handler(line, model) + if err != nil { + if err == io.EOF { + return nil + } + } + } +} + +// SetMockErr sets string to be returned by of the mock during testing +func (a *AdapterMock) SetMockErr(errorToSet string) { + a.errorValue = errorToSet +} + +// GetMockErr returns a mock error or nil +func (a *AdapterMock) GetMockErr() error { + var returnError error + if a.errorValue != "" { + returnError = errors.New(a.errorValue) + } + return returnError +} + +// AddPolicy adds a policy rule to the storage. +func (a *AdapterMock) AddPolicy(sec string, ptype string, rule []string) error { + return a.GetMockErr() +} + +// RemovePolicy removes a policy rule from the storage. +func (a *AdapterMock) RemovePolicy(sec string, ptype string, rule []string) error { + return a.GetMockErr() +} + +// RemoveFilteredPolicy removes policy rules that match the filter from the storage. +func (a *AdapterMock) RemoveFilteredPolicy(sec string, ptype string, fieldIndex int, fieldValues ...string) error { + return a.GetMockErr() +} diff --git a/vendor/github.com/casbin/casbin/persist/watcher.go b/vendor/github.com/casbin/casbin/persist/watcher.go new file mode 100644 index 000000000..089fbc0f2 --- /dev/null +++ b/vendor/github.com/casbin/casbin/persist/watcher.go @@ -0,0 +1,27 @@ +// Copyright 2017 The casbin Authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package persist + +// Watcher is the interface for Casbin watchers. +type Watcher interface { + // SetUpdateCallback sets the callback function that the watcher will call + // when the policy in DB has been changed by other instances. + // A classic callback is Enforcer.LoadPolicy(). + SetUpdateCallback(func(string)) error + // Update calls the update callback of other instances to synchronize their policy. + // It is usually called after changing the policy in DB, like Enforcer.SavePolicy(), + // Enforcer.AddPolicy(), Enforcer.RemovePolicy(), etc. + Update() error +} diff --git a/vendor/github.com/casbin/casbin/rbac/default-role-manager/role_manager.go b/vendor/github.com/casbin/casbin/rbac/default-role-manager/role_manager.go new file mode 100644 index 000000000..e7eb901f5 --- /dev/null +++ b/vendor/github.com/casbin/casbin/rbac/default-role-manager/role_manager.go @@ -0,0 +1,257 @@ +// Copyright 2017 The casbin Authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package defaultrolemanager + +import ( + "errors" + "sync" + + "github.com/casbin/casbin/log" + "github.com/casbin/casbin/rbac" +) + +// RoleManager provides a default implementation for the RoleManager interface +type RoleManager struct { + allRoles *sync.Map + maxHierarchyLevel int +} + +// NewRoleManager is the constructor for creating an instance of the +// default RoleManager implementation. +func NewRoleManager(maxHierarchyLevel int) rbac.RoleManager { + rm := RoleManager{} + rm.allRoles = &sync.Map{} + rm.maxHierarchyLevel = maxHierarchyLevel + return &rm +} + +func (rm *RoleManager) hasRole(name string) bool { + _, ok := rm.allRoles.Load(name) + return ok +} + +func (rm *RoleManager) createRole(name string) *Role { + role, _ := rm.allRoles.LoadOrStore(name, newRole(name)) + return role.(*Role) +} + +// Clear clears all stored data and resets the role manager to the initial state. +func (rm *RoleManager) Clear() error { + rm.allRoles = &sync.Map{} + return nil +} + +// AddLink adds the inheritance link between role: name1 and role: name2. +// aka role: name1 inherits role: name2. +// domain is a prefix to the roles. +func (rm *RoleManager) AddLink(name1 string, name2 string, domain ...string) error { + if len(domain) == 1 { + name1 = domain[0] + "::" + name1 + name2 = domain[0] + "::" + name2 + } else if len(domain) > 1 { + return errors.New("error: domain should be 1 parameter") + } + + role1 := rm.createRole(name1) + role2 := rm.createRole(name2) + role1.addRole(role2) + return nil +} + +// DeleteLink deletes the inheritance link between role: name1 and role: name2. +// aka role: name1 does not inherit role: name2 any more. +// domain is a prefix to the roles. +func (rm *RoleManager) DeleteLink(name1 string, name2 string, domain ...string) error { + if len(domain) == 1 { + name1 = domain[0] + "::" + name1 + name2 = domain[0] + "::" + name2 + } else if len(domain) > 1 { + return errors.New("error: domain should be 1 parameter") + } + + if !rm.hasRole(name1) || !rm.hasRole(name2) { + return errors.New("error: name1 or name2 does not exist") + } + + role1 := rm.createRole(name1) + role2 := rm.createRole(name2) + role1.deleteRole(role2) + return nil +} + +// HasLink determines whether role: name1 inherits role: name2. +// domain is a prefix to the roles. +func (rm *RoleManager) HasLink(name1 string, name2 string, domain ...string) (bool, error) { + if len(domain) == 1 { + name1 = domain[0] + "::" + name1 + name2 = domain[0] + "::" + name2 + } else if len(domain) > 1 { + return false, errors.New("error: domain should be 1 parameter") + } + + if name1 == name2 { + return true, nil + } + + if !rm.hasRole(name1) || !rm.hasRole(name2) { + return false, nil + } + + role1 := rm.createRole(name1) + return role1.hasRole(name2, rm.maxHierarchyLevel), nil +} + +// GetRoles gets the roles that a subject inherits. +// domain is a prefix to the roles. +func (rm *RoleManager) GetRoles(name string, domain ...string) ([]string, error) { + if len(domain) == 1 { + name = domain[0] + "::" + name + } else if len(domain) > 1 { + return nil, errors.New("error: domain should be 1 parameter") + } + + if !rm.hasRole(name) { + return []string{}, nil + } + + roles := rm.createRole(name).getRoles() + if len(domain) == 1 { + for i := range roles { + roles[i] = roles[i][len(domain[0])+2:] + } + } + return roles, nil +} + +// GetUsers gets the users that inherits a subject. +// domain is an unreferenced parameter here, may be used in other implementations. +func (rm *RoleManager) GetUsers(name string, domain ...string) ([]string, error) { + if !rm.hasRole(name) { + return nil, errors.New("error: name does not exist") + } + + names := []string{} + rm.allRoles.Range(func(_, value interface{}) bool { + role := value.(*Role) + if role.hasDirectRole(name) { + names = append(names, role.name) + } + return true + }) + return names, nil +} + +// PrintRoles prints all the roles to log. +func (rm *RoleManager) PrintRoles() error { + line := "" + rm.allRoles.Range(func(_, value interface{}) bool { + if text := value.(*Role).toString(); text != "" { + if line == "" { + line = text + } else { + line += ", " + text + } + } + return true + }) + log.LogPrint(line) + return nil +} + +// Role represents the data structure for a role in RBAC. +type Role struct { + name string + roles []*Role +} + +func newRole(name string) *Role { + r := Role{} + r.name = name + return &r +} + +func (r *Role) addRole(role *Role) { + for _, rr := range r.roles { + if rr.name == role.name { + return + } + } + + r.roles = append(r.roles, role) +} + +func (r *Role) deleteRole(role *Role) { + for i, rr := range r.roles { + if rr.name == role.name { + r.roles = append(r.roles[:i], r.roles[i+1:]...) + return + } + } +} + +func (r *Role) hasRole(name string, hierarchyLevel int) bool { + if r.name == name { + return true + } + + if hierarchyLevel <= 0 { + return false + } + + for _, role := range r.roles { + if role.hasRole(name, hierarchyLevel-1) { + return true + } + } + return false +} + +func (r *Role) hasDirectRole(name string) bool { + for _, role := range r.roles { + if role.name == name { + return true + } + } + + return false +} + +func (r *Role) toString() string { + names := "" + if len(r.roles) == 0 { + return "" + } + for i, role := range r.roles { + if i == 0 { + names += role.name + } else { + names += ", " + role.name + } + } + + if len(r.roles) == 1 { + return r.name + " < " + names + } else { + return r.name + " < (" + names + ")" + } +} + +func (r *Role) getRoles() []string { + names := []string{} + for _, role := range r.roles { + names = append(names, role.name) + } + return names +} diff --git a/vendor/github.com/casbin/casbin/rbac/role_manager.go b/vendor/github.com/casbin/casbin/rbac/role_manager.go new file mode 100644 index 000000000..b853bc931 --- /dev/null +++ b/vendor/github.com/casbin/casbin/rbac/role_manager.go @@ -0,0 +1,38 @@ +// Copyright 2017 The casbin Authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package rbac + +// RoleManager provides interface to define the operations for managing roles. +type RoleManager interface { + // Clear clears all stored data and resets the role manager to the initial state. + Clear() error + // AddLink adds the inheritance link between two roles. role: name1 and role: name2. + // domain is a prefix to the roles (can be used for other purposes). + AddLink(name1 string, name2 string, domain ...string) error + // DeleteLink deletes the inheritance link between two roles. role: name1 and role: name2. + // domain is a prefix to the roles (can be used for other purposes). + DeleteLink(name1 string, name2 string, domain ...string) error + // HasLink determines whether a link exists between two roles. role: name1 inherits role: name2. + // domain is a prefix to the roles (can be used for other purposes). + HasLink(name1 string, name2 string, domain ...string) (bool, error) + // GetRoles gets the roles that a user inherits. + // domain is a prefix to the roles (can be used for other purposes). + GetRoles(name string, domain ...string) ([]string, error) + // GetUsers gets the users that inherits a role. + // domain is a prefix to the users (can be used for other purposes). + GetUsers(name string, domain ...string) ([]string, error) + // PrintRoles prints all the roles to log. + PrintRoles() error +} diff --git a/vendor/github.com/casbin/casbin/rbac_api.go b/vendor/github.com/casbin/casbin/rbac_api.go new file mode 100644 index 000000000..33bf952b7 --- /dev/null +++ b/vendor/github.com/casbin/casbin/rbac_api.go @@ -0,0 +1,184 @@ +// Copyright 2017 The casbin Authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package casbin + +// GetRolesForUser gets the roles that a user has. +func (e *Enforcer) GetRolesForUser(name string) []string { + res, _ := e.model["g"]["g"].RM.GetRoles(name) + return res +} + +// GetUsersForRole gets the users that has a role. +func (e *Enforcer) GetUsersForRole(name string) []string { + res, _ := e.model["g"]["g"].RM.GetUsers(name) + return res +} + +// HasRoleForUser determines whether a user has a role. +func (e *Enforcer) HasRoleForUser(name string, role string) bool { + roles := e.GetRolesForUser(name) + + hasRole := false + for _, r := range roles { + if r == role { + hasRole = true + break + } + } + + return hasRole +} + +// AddRoleForUser adds a role for a user. +// Returns false if the user already has the role (aka not affected). +func (e *Enforcer) AddRoleForUser(user string, role string) bool { + return e.AddGroupingPolicy(user, role) +} + +// DeleteRoleForUser deletes a role for a user. +// Returns false if the user does not have the role (aka not affected). +func (e *Enforcer) DeleteRoleForUser(user string, role string) bool { + return e.RemoveGroupingPolicy(user, role) +} + +// DeleteRolesForUser deletes all roles for a user. +// Returns false if the user does not have any roles (aka not affected). +func (e *Enforcer) DeleteRolesForUser(user string) bool { + return e.RemoveFilteredGroupingPolicy(0, user) +} + +// DeleteUser deletes a user. +// Returns false if the user does not exist (aka not affected). +func (e *Enforcer) DeleteUser(user string) bool { + return e.RemoveFilteredGroupingPolicy(0, user) +} + +// DeleteRole deletes a role. +func (e *Enforcer) DeleteRole(role string) { + e.RemoveFilteredGroupingPolicy(1, role) + e.RemoveFilteredPolicy(0, role) +} + +// DeletePermission deletes a permission. +// Returns false if the permission does not exist (aka not affected). +func (e *Enforcer) DeletePermission(permission ...string) bool { + return e.RemoveFilteredPolicy(1, permission...) +} + +// AddPermissionForUser adds a permission for a user or role. +// Returns false if the user or role already has the permission (aka not affected). +func (e *Enforcer) AddPermissionForUser(user string, permission ...string) bool { + params := make([]interface{}, 0, len(permission)+1) + + params = append(params, user) + for _, perm := range permission { + params = append(params, perm) + } + + return e.AddPolicy(params...) +} + +// DeletePermissionForUser deletes a permission for a user or role. +// Returns false if the user or role does not have the permission (aka not affected). +func (e *Enforcer) DeletePermissionForUser(user string, permission ...string) bool { + params := make([]interface{}, 0, len(permission)+1) + + params = append(params, user) + for _, perm := range permission { + params = append(params, perm) + } + + return e.RemovePolicy(params...) +} + +// DeletePermissionsForUser deletes permissions for a user or role. +// Returns false if the user or role does not have any permissions (aka not affected). +func (e *Enforcer) DeletePermissionsForUser(user string) bool { + return e.RemoveFilteredPolicy(0, user) +} + +// GetPermissionsForUser gets permissions for a user or role. +func (e *Enforcer) GetPermissionsForUser(user string) [][]string { + return e.GetFilteredPolicy(0, user) +} + +// HasPermissionForUser determines whether a user has a permission. +func (e *Enforcer) HasPermissionForUser(user string, permission ...string) bool { + params := make([]interface{}, 0, len(permission)+1) + + params = append(params, user) + for _, perm := range permission { + params = append(params, perm) + } + + return e.HasPolicy(params...) +} + +// GetImplicitRolesForUser gets implicit roles that a user has. +// Compared to GetRolesForUser(), this function retrieves indirect roles besides direct roles. +// For example: +// g, alice, role:admin +// g, role:admin, role:user +// +// GetRolesForUser("alice") can only get: ["role:admin"]. +// But GetImplicitRolesForUser("alice") will get: ["role:admin", "role:user"]. +func (e *Enforcer) GetImplicitRolesForUser(name string) []string { + res := []string{} + roleSet := make(map[string]bool) + roleSet[name] = true + + q := make([]string, 0) + q = append(q, name) + + for len(q) > 0 { + name := q[0] + q = q[1:] + + roles, err := e.rm.GetRoles(name) + if err != nil { + panic(err) + } + for _, r := range roles { + if _, ok := roleSet[r]; !ok { + res = append(res, r) + q = append(q, r) + roleSet[r] = true + } + } + } + + return res +} + +// GetImplicitPermissionsForUser gets implicit permissions for a user or role. +// Compared to GetPermissionsForUser(), this function retrieves permissions for inherited roles. +// For example: +// p, admin, data1, read +// p, alice, data2, read +// g, alice, admin +// +// GetPermissionsForUser("alice") can only get: [["alice", "data2", "read"]]. +// But GetImplicitPermissionsForUser("alice") will get: [["admin", "data1", "read"], ["alice", "data2", "read"]]. +func (e *Enforcer) GetImplicitPermissionsForUser(user string) [][]string { + roles := e.GetImplicitRolesForUser(user) + roles = append([]string{user}, roles...) + + res := [][]string{} + for _, role := range roles { + permissions := e.GetPermissionsForUser(role) + res = append(res, permissions...) + } + return res +} diff --git a/vendor/github.com/casbin/casbin/rbac_api_synced.go b/vendor/github.com/casbin/casbin/rbac_api_synced.go new file mode 100644 index 000000000..119ce9f8b --- /dev/null +++ b/vendor/github.com/casbin/casbin/rbac_api_synced.go @@ -0,0 +1,121 @@ +// Copyright 2017 The casbin Authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package casbin + +// GetRolesForUser gets the roles that a user has. +func (e *SyncedEnforcer) GetRolesForUser(name string) []string { + e.m.Lock() + defer e.m.Unlock() + return e.Enforcer.GetRolesForUser(name) +} + +// GetUsersForRole gets the users that has a role. +func (e *SyncedEnforcer) GetUsersForRole(name string) []string { + e.m.Lock() + defer e.m.Unlock() + return e.Enforcer.GetUsersForRole(name) +} + +// HasRoleForUser determines whether a user has a role. +func (e *SyncedEnforcer) HasRoleForUser(name string, role string) bool { + e.m.Lock() + defer e.m.Unlock() + return e.Enforcer.HasRoleForUser(name, role) +} + +// AddRoleForUser adds a role for a user. +// Returns false if the user already has the role (aka not affected). +func (e *SyncedEnforcer) AddRoleForUser(user string, role string) bool { + e.m.Lock() + defer e.m.Unlock() + return e.Enforcer.AddRoleForUser(user, role) +} + +// DeleteRoleForUser deletes a role for a user. +// Returns false if the user does not have the role (aka not affected). +func (e *SyncedEnforcer) DeleteRoleForUser(user string, role string) bool { + e.m.Lock() + defer e.m.Unlock() + return e.Enforcer.DeleteRoleForUser(user, role) +} + +// DeleteRolesForUser deletes all roles for a user. +// Returns false if the user does not have any roles (aka not affected). +func (e *SyncedEnforcer) DeleteRolesForUser(user string) bool { + e.m.Lock() + defer e.m.Unlock() + return e.Enforcer.DeleteRolesForUser(user) +} + +// DeleteUser deletes a user. +// Returns false if the user does not exist (aka not affected). +func (e *SyncedEnforcer) DeleteUser(user string) bool { + e.m.Lock() + defer e.m.Unlock() + return e.Enforcer.DeleteUser(user) +} + +// DeleteRole deletes a role. +func (e *SyncedEnforcer) DeleteRole(role string) { + e.m.Lock() + defer e.m.Unlock() + e.Enforcer.DeleteRole(role) +} + +// DeletePermission deletes a permission. +// Returns false if the permission does not exist (aka not affected). +func (e *SyncedEnforcer) DeletePermission(permission ...string) bool { + e.m.Lock() + defer e.m.Unlock() + return e.Enforcer.DeletePermission(permission...) +} + +// AddPermissionForUser adds a permission for a user or role. +// Returns false if the user or role already has the permission (aka not affected). +func (e *SyncedEnforcer) AddPermissionForUser(user string, permission ...string) bool { + e.m.Lock() + defer e.m.Unlock() + return e.Enforcer.AddPermissionForUser(user, permission...) +} + +// DeletePermissionForUser deletes a permission for a user or role. +// Returns false if the user or role does not have the permission (aka not affected). +func (e *SyncedEnforcer) DeletePermissionForUser(user string, permission ...string) bool { + e.m.Lock() + defer e.m.Unlock() + return e.Enforcer.DeletePermissionForUser(user, permission...) +} + +// DeletePermissionsForUser deletes permissions for a user or role. +// Returns false if the user or role does not have any permissions (aka not affected). +func (e *SyncedEnforcer) DeletePermissionsForUser(user string) bool { + e.m.Lock() + defer e.m.Unlock() + return e.Enforcer.DeletePermissionsForUser(user) +} + +// GetPermissionsForUser gets permissions for a user or role. +func (e *SyncedEnforcer) GetPermissionsForUser(user string) [][]string { + e.m.Lock() + defer e.m.Unlock() + return e.Enforcer.GetPermissionsForUser(user) +} + +// HasPermissionForUser determines whether a user has a permission. +func (e *SyncedEnforcer) HasPermissionForUser(user string, permission ...string) bool { + e.m.Lock() + defer e.m.Unlock() + return e.Enforcer.HasPermissionForUser(user, permission...) +} diff --git a/vendor/github.com/casbin/casbin/rbac_api_with_domains.go b/vendor/github.com/casbin/casbin/rbac_api_with_domains.go new file mode 100644 index 000000000..d6af0d6d6 --- /dev/null +++ b/vendor/github.com/casbin/casbin/rbac_api_with_domains.go @@ -0,0 +1,38 @@ +// Copyright 2017 The casbin Authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package casbin + +// GetRolesForUserInDomain gets the roles that a user has inside a domain. +func (e *Enforcer) GetRolesForUserInDomain(name string, domain string) []string { + res, _ := e.model["g"]["g"].RM.GetRoles(name, domain) + return res +} + +// GetPermissionsForUserInDomain gets permissions for a user or role inside a domain. +func (e *Enforcer) GetPermissionsForUserInDomain(user string, domain string) [][]string { + return e.GetFilteredPolicy(0, user, domain) +} + +// AddRoleForUserInDomain adds a role for a user inside a domain. +// Returns false if the user already has the role (aka not affected). +func (e *Enforcer) AddRoleForUserInDomain(user string, role string, domain string) bool { + return e.AddGroupingPolicy(user, role, domain) +} + +// DeleteRoleForUserInDomain deletes a role for a user inside a domain. +// Returns false if the user does not have the role (aka not affected). +func (e *Enforcer) DeleteRoleForUserInDomain(user string, role string, domain string) bool { + return e.RemoveGroupingPolicy(user, role, domain) +} diff --git a/vendor/github.com/casbin/casbin/util/builtin_operators.go b/vendor/github.com/casbin/casbin/util/builtin_operators.go new file mode 100644 index 000000000..79a8825e5 --- /dev/null +++ b/vendor/github.com/casbin/casbin/util/builtin_operators.go @@ -0,0 +1,160 @@ +// Copyright 2017 The casbin Authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package util + +import ( + "net" + "regexp" + "strings" + + "github.com/casbin/casbin/rbac" +) + +// KeyMatch determines whether key1 matches the pattern of key2 (similar to RESTful path), key2 can contain a *. +// For example, "/foo/bar" matches "/foo/*" +func KeyMatch(key1 string, key2 string) bool { + i := strings.Index(key2, "*") + if i == -1 { + return key1 == key2 + } + + if len(key1) > i { + return key1[:i] == key2[:i] + } + return key1 == key2[:i] +} + +// KeyMatchFunc is the wrapper for KeyMatch. +func KeyMatchFunc(args ...interface{}) (interface{}, error) { + name1 := args[0].(string) + name2 := args[1].(string) + + return bool(KeyMatch(name1, name2)), nil +} + +// KeyMatch2 determines whether key1 matches the pattern of key2 (similar to RESTful path), key2 can contain a *. +// For example, "/foo/bar" matches "/foo/*", "/resource1" matches "/:resource" +func KeyMatch2(key1 string, key2 string) bool { + key2 = strings.Replace(key2, "/*", "/.*", -1) + + re := regexp.MustCompile(`(.*):[^/]+(.*)`) + for { + if !strings.Contains(key2, "/:") { + break + } + + key2 = "^" + re.ReplaceAllString(key2, "$1[^/]+$2") + "$" + } + + return RegexMatch(key1, key2) +} + +// KeyMatch2Func is the wrapper for KeyMatch2. +func KeyMatch2Func(args ...interface{}) (interface{}, error) { + name1 := args[0].(string) + name2 := args[1].(string) + + return bool(KeyMatch2(name1, name2)), nil +} + +// KeyMatch3 determines whether key1 matches the pattern of key2 (similar to RESTful path), key2 can contain a *. +// For example, "/foo/bar" matches "/foo/*", "/resource1" matches "/{resource}" +func KeyMatch3(key1 string, key2 string) bool { + key2 = strings.Replace(key2, "/*", "/.*", -1) + + re := regexp.MustCompile(`(.*)\{[^/]+\}(.*)`) + for { + if !strings.Contains(key2, "/{") { + break + } + + key2 = re.ReplaceAllString(key2, "$1[^/]+$2") + } + + return RegexMatch(key1, key2) +} + +// KeyMatch3Func is the wrapper for KeyMatch3. +func KeyMatch3Func(args ...interface{}) (interface{}, error) { + name1 := args[0].(string) + name2 := args[1].(string) + + return bool(KeyMatch3(name1, name2)), nil +} + +// RegexMatch determines whether key1 matches the pattern of key2 in regular expression. +func RegexMatch(key1 string, key2 string) bool { + res, err := regexp.MatchString(key2, key1) + if err != nil { + panic(err) + } + return res +} + +// RegexMatchFunc is the wrapper for RegexMatch. +func RegexMatchFunc(args ...interface{}) (interface{}, error) { + name1 := args[0].(string) + name2 := args[1].(string) + + return bool(RegexMatch(name1, name2)), nil +} + +// IPMatch determines whether IP address ip1 matches the pattern of IP address ip2, ip2 can be an IP address or a CIDR pattern. +// For example, "192.168.2.123" matches "192.168.2.0/24" +func IPMatch(ip1 string, ip2 string) bool { + objIP1 := net.ParseIP(ip1) + if objIP1 == nil { + panic("invalid argument: ip1 in IPMatch() function is not an IP address.") + } + + _, cidr, err := net.ParseCIDR(ip2) + if err != nil { + objIP2 := net.ParseIP(ip2) + if objIP2 == nil { + panic("invalid argument: ip2 in IPMatch() function is neither an IP address nor a CIDR.") + } + + return objIP1.Equal(objIP2) + } + + return cidr.Contains(objIP1) +} + +// IPMatchFunc is the wrapper for IPMatch. +func IPMatchFunc(args ...interface{}) (interface{}, error) { + ip1 := args[0].(string) + ip2 := args[1].(string) + + return bool(IPMatch(ip1, ip2)), nil +} + +// GenerateGFunction is the factory method of the g(_, _) function. +func GenerateGFunction(rm rbac.RoleManager) func(args ...interface{}) (interface{}, error) { + return func(args ...interface{}) (interface{}, error) { + name1 := args[0].(string) + name2 := args[1].(string) + + if rm == nil { + return name1 == name2, nil + } else if len(args) == 2 { + res, _ := rm.HasLink(name1, name2) + return res, nil + } else { + domain := args[2].(string) + res, _ := rm.HasLink(name1, name2, domain) + return res, nil + } + } +} diff --git a/vendor/github.com/casbin/casbin/util/util.go b/vendor/github.com/casbin/casbin/util/util.go new file mode 100644 index 000000000..4ce033d5a --- /dev/null +++ b/vendor/github.com/casbin/casbin/util/util.go @@ -0,0 +1,105 @@ +// Copyright 2017 The casbin Authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package util + +import ( + "sort" + "strings" +) + +// EscapeAssertion escapes the dots in the assertion, because the expression evaluation doesn't support such variable names. +func EscapeAssertion(s string) string { + s = strings.Replace(s, "r.", "r_", -1) + s = strings.Replace(s, "p.", "p_", -1) + return s +} + +// RemoveComments removes the comments starting with # in the text. +func RemoveComments(s string) string { + pos := strings.Index(s, "#") + if pos == -1 { + return s + } + return strings.TrimSpace(s[0:pos]) +} + +// ArrayEquals determines whether two string arrays are identical. +func ArrayEquals(a []string, b []string) bool { + if len(a) != len(b) { + return false + } + + for i, v := range a { + if v != b[i] { + return false + } + } + return true +} + +// Array2DEquals determines whether two 2-dimensional string arrays are identical. +func Array2DEquals(a [][]string, b [][]string) bool { + if len(a) != len(b) { + return false + } + + for i, v := range a { + if !ArrayEquals(v, b[i]) { + return false + } + } + return true +} + +// ArrayRemoveDuplicates removes any duplicated elements in a string array. +func ArrayRemoveDuplicates(s *[]string) { + found := make(map[string]bool) + j := 0 + for i, x := range *s { + if !found[x] { + found[x] = true + (*s)[j] = (*s)[i] + j++ + } + } + *s = (*s)[:j] +} + +// ArrayToString gets a printable string for a string array. +func ArrayToString(s []string) string { + return strings.Join(s, ", ") +} + +// ParamsToString gets a printable string for variable number of parameters. +func ParamsToString(s ...string) string { + return strings.Join(s, ", ") +} + +// SetEquals determines whether two string sets are identical. +func SetEquals(a []string, b []string) bool { + if len(a) != len(b) { + return false + } + + sort.Strings(a) + sort.Strings(b) + + for i, v := range a { + if v != b[i] { + return false + } + } + return true +} diff --git a/vendor/golang.org/x/sys/unix/mkerrors.sh b/vendor/golang.org/x/sys/unix/mkerrors.sh index aa860fa26..955dd50f9 100755 --- a/vendor/golang.org/x/sys/unix/mkerrors.sh +++ b/vendor/golang.org/x/sys/unix/mkerrors.sh @@ -187,6 +187,7 @@ struct ltchars { #include #include #include +#include #include #include #include @@ -500,6 +501,7 @@ ccflags="$@" $2 ~ /^(HDIO|WIN|SMART)_/ || $2 !~ "WMESGLEN" && $2 ~ /^W[A-Z0-9]+$/ || + $2 ~/^PPPIOC/ || $2 ~ /^BLK[A-Z]*(GET$|SET$|BUF$|PART$|SIZE)/ {printf("\t%s = C.%s\n", $2, $2)} $2 ~ /^__WCOREFLAG$/ {next} $2 ~ /^__W[A-Z0-9]+$/ {printf("\t%s = C.%s\n", substr($2,3), $2)} diff --git a/vendor/golang.org/x/sys/unix/zerrors_linux_386.go b/vendor/golang.org/x/sys/unix/zerrors_linux_386.go index 673152b94..db3c31ef8 100644 --- a/vendor/golang.org/x/sys/unix/zerrors_linux_386.go +++ b/vendor/golang.org/x/sys/unix/zerrors_linux_386.go @@ -1293,6 +1293,36 @@ const ( PERF_EVENT_IOC_SET_FILTER = 0x40042406 PERF_EVENT_IOC_SET_OUTPUT = 0x2405 PIPEFS_MAGIC = 0x50495045 + PPPIOCATTACH = 0x4004743d + PPPIOCATTCHAN = 0x40047438 + PPPIOCCONNECT = 0x4004743a + PPPIOCDETACH = 0x4004743c + PPPIOCDISCONN = 0x7439 + PPPIOCGASYNCMAP = 0x80047458 + PPPIOCGCHAN = 0x80047437 + PPPIOCGDEBUG = 0x80047441 + PPPIOCGFLAGS = 0x8004745a + PPPIOCGIDLE = 0x8008743f + PPPIOCGL2TPSTATS = 0x80487436 + PPPIOCGMRU = 0x80047453 + PPPIOCGNPMODE = 0xc008744c + PPPIOCGRASYNCMAP = 0x80047455 + PPPIOCGUNIT = 0x80047456 + PPPIOCGXASYNCMAP = 0x80207450 + PPPIOCNEWUNIT = 0xc004743e + PPPIOCSACTIVE = 0x40087446 + PPPIOCSASYNCMAP = 0x40047457 + PPPIOCSCOMPRESS = 0x400c744d + PPPIOCSDEBUG = 0x40047440 + PPPIOCSFLAGS = 0x40047459 + PPPIOCSMAXCID = 0x40047451 + PPPIOCSMRRU = 0x4004743b + PPPIOCSMRU = 0x40047452 + PPPIOCSNPMODE = 0x4008744b + PPPIOCSPASS = 0x40087447 + PPPIOCSRASYNCMAP = 0x40047454 + PPPIOCSXASYNCMAP = 0x4020744f + PPPIOCXFERUNIT = 0x744e PRIO_PGRP = 0x1 PRIO_PROCESS = 0x0 PRIO_USER = 0x2 @@ -1696,6 +1726,7 @@ const ( SCM_TIMESTAMPNS = 0x23 SCM_TXTIME = 0x3d SCM_WIFI_STATUS = 0x29 + SC_LOG_FLUSH = 0x100000 SECCOMP_MODE_DISABLED = 0x0 SECCOMP_MODE_FILTER = 0x2 SECCOMP_MODE_STRICT = 0x1 @@ -1751,6 +1782,9 @@ const ( SIOCGMIIPHY = 0x8947 SIOCGMIIREG = 0x8948 SIOCGPGRP = 0x8904 + SIOCGPPPCSTATS = 0x89f2 + SIOCGPPPSTATS = 0x89f0 + SIOCGPPPVER = 0x89f1 SIOCGRARP = 0x8961 SIOCGSKNS = 0x894c SIOCGSTAMP = 0x8906 diff --git a/vendor/golang.org/x/sys/unix/zerrors_linux_amd64.go b/vendor/golang.org/x/sys/unix/zerrors_linux_amd64.go index 5735bcf3c..4785835b6 100644 --- a/vendor/golang.org/x/sys/unix/zerrors_linux_amd64.go +++ b/vendor/golang.org/x/sys/unix/zerrors_linux_amd64.go @@ -1293,6 +1293,36 @@ const ( PERF_EVENT_IOC_SET_FILTER = 0x40082406 PERF_EVENT_IOC_SET_OUTPUT = 0x2405 PIPEFS_MAGIC = 0x50495045 + PPPIOCATTACH = 0x4004743d + PPPIOCATTCHAN = 0x40047438 + PPPIOCCONNECT = 0x4004743a + PPPIOCDETACH = 0x4004743c + PPPIOCDISCONN = 0x7439 + PPPIOCGASYNCMAP = 0x80047458 + PPPIOCGCHAN = 0x80047437 + PPPIOCGDEBUG = 0x80047441 + PPPIOCGFLAGS = 0x8004745a + PPPIOCGIDLE = 0x8010743f + PPPIOCGL2TPSTATS = 0x80487436 + PPPIOCGMRU = 0x80047453 + PPPIOCGNPMODE = 0xc008744c + PPPIOCGRASYNCMAP = 0x80047455 + PPPIOCGUNIT = 0x80047456 + PPPIOCGXASYNCMAP = 0x80207450 + PPPIOCNEWUNIT = 0xc004743e + PPPIOCSACTIVE = 0x40107446 + PPPIOCSASYNCMAP = 0x40047457 + PPPIOCSCOMPRESS = 0x4010744d + PPPIOCSDEBUG = 0x40047440 + PPPIOCSFLAGS = 0x40047459 + PPPIOCSMAXCID = 0x40047451 + PPPIOCSMRRU = 0x4004743b + PPPIOCSMRU = 0x40047452 + PPPIOCSNPMODE = 0x4008744b + PPPIOCSPASS = 0x40107447 + PPPIOCSRASYNCMAP = 0x40047454 + PPPIOCSXASYNCMAP = 0x4020744f + PPPIOCXFERUNIT = 0x744e PRIO_PGRP = 0x1 PRIO_PROCESS = 0x0 PRIO_USER = 0x2 @@ -1697,6 +1727,7 @@ const ( SCM_TIMESTAMPNS = 0x23 SCM_TXTIME = 0x3d SCM_WIFI_STATUS = 0x29 + SC_LOG_FLUSH = 0x100000 SECCOMP_MODE_DISABLED = 0x0 SECCOMP_MODE_FILTER = 0x2 SECCOMP_MODE_STRICT = 0x1 @@ -1752,6 +1783,9 @@ const ( SIOCGMIIPHY = 0x8947 SIOCGMIIREG = 0x8948 SIOCGPGRP = 0x8904 + SIOCGPPPCSTATS = 0x89f2 + SIOCGPPPSTATS = 0x89f0 + SIOCGPPPVER = 0x89f1 SIOCGRARP = 0x8961 SIOCGSKNS = 0x894c SIOCGSTAMP = 0x8906 diff --git a/vendor/golang.org/x/sys/unix/zerrors_linux_arm.go b/vendor/golang.org/x/sys/unix/zerrors_linux_arm.go index d8e8442e5..5e902423a 100644 --- a/vendor/golang.org/x/sys/unix/zerrors_linux_arm.go +++ b/vendor/golang.org/x/sys/unix/zerrors_linux_arm.go @@ -1291,6 +1291,36 @@ const ( PERF_EVENT_IOC_SET_FILTER = 0x40042406 PERF_EVENT_IOC_SET_OUTPUT = 0x2405 PIPEFS_MAGIC = 0x50495045 + PPPIOCATTACH = 0x4004743d + PPPIOCATTCHAN = 0x40047438 + PPPIOCCONNECT = 0x4004743a + PPPIOCDETACH = 0x4004743c + PPPIOCDISCONN = 0x7439 + PPPIOCGASYNCMAP = 0x80047458 + PPPIOCGCHAN = 0x80047437 + PPPIOCGDEBUG = 0x80047441 + PPPIOCGFLAGS = 0x8004745a + PPPIOCGIDLE = 0x8008743f + PPPIOCGL2TPSTATS = 0x80487436 + PPPIOCGMRU = 0x80047453 + PPPIOCGNPMODE = 0xc008744c + PPPIOCGRASYNCMAP = 0x80047455 + PPPIOCGUNIT = 0x80047456 + PPPIOCGXASYNCMAP = 0x80207450 + PPPIOCNEWUNIT = 0xc004743e + PPPIOCSACTIVE = 0x40087446 + PPPIOCSASYNCMAP = 0x40047457 + PPPIOCSCOMPRESS = 0x400c744d + PPPIOCSDEBUG = 0x40047440 + PPPIOCSFLAGS = 0x40047459 + PPPIOCSMAXCID = 0x40047451 + PPPIOCSMRRU = 0x4004743b + PPPIOCSMRU = 0x40047452 + PPPIOCSNPMODE = 0x4008744b + PPPIOCSPASS = 0x40087447 + PPPIOCSRASYNCMAP = 0x40047454 + PPPIOCSXASYNCMAP = 0x4020744f + PPPIOCXFERUNIT = 0x744e PRIO_PGRP = 0x1 PRIO_PROCESS = 0x0 PRIO_USER = 0x2 @@ -1703,6 +1733,7 @@ const ( SCM_TIMESTAMPNS = 0x23 SCM_TXTIME = 0x3d SCM_WIFI_STATUS = 0x29 + SC_LOG_FLUSH = 0x100000 SECCOMP_MODE_DISABLED = 0x0 SECCOMP_MODE_FILTER = 0x2 SECCOMP_MODE_STRICT = 0x1 @@ -1758,6 +1789,9 @@ const ( SIOCGMIIPHY = 0x8947 SIOCGMIIREG = 0x8948 SIOCGPGRP = 0x8904 + SIOCGPPPCSTATS = 0x89f2 + SIOCGPPPSTATS = 0x89f0 + SIOCGPPPVER = 0x89f1 SIOCGRARP = 0x8961 SIOCGSKNS = 0x894c SIOCGSTAMP = 0x8906 diff --git a/vendor/golang.org/x/sys/unix/zerrors_linux_arm64.go b/vendor/golang.org/x/sys/unix/zerrors_linux_arm64.go index 5d79b789b..ebe9d8b41 100644 --- a/vendor/golang.org/x/sys/unix/zerrors_linux_arm64.go +++ b/vendor/golang.org/x/sys/unix/zerrors_linux_arm64.go @@ -1294,6 +1294,36 @@ const ( PERF_EVENT_IOC_SET_FILTER = 0x40082406 PERF_EVENT_IOC_SET_OUTPUT = 0x2405 PIPEFS_MAGIC = 0x50495045 + PPPIOCATTACH = 0x4004743d + PPPIOCATTCHAN = 0x40047438 + PPPIOCCONNECT = 0x4004743a + PPPIOCDETACH = 0x4004743c + PPPIOCDISCONN = 0x7439 + PPPIOCGASYNCMAP = 0x80047458 + PPPIOCGCHAN = 0x80047437 + PPPIOCGDEBUG = 0x80047441 + PPPIOCGFLAGS = 0x8004745a + PPPIOCGIDLE = 0x8010743f + PPPIOCGL2TPSTATS = 0x80487436 + PPPIOCGMRU = 0x80047453 + PPPIOCGNPMODE = 0xc008744c + PPPIOCGRASYNCMAP = 0x80047455 + PPPIOCGUNIT = 0x80047456 + PPPIOCGXASYNCMAP = 0x80207450 + PPPIOCNEWUNIT = 0xc004743e + PPPIOCSACTIVE = 0x40107446 + PPPIOCSASYNCMAP = 0x40047457 + PPPIOCSCOMPRESS = 0x4010744d + PPPIOCSDEBUG = 0x40047440 + PPPIOCSFLAGS = 0x40047459 + PPPIOCSMAXCID = 0x40047451 + PPPIOCSMRRU = 0x4004743b + PPPIOCSMRU = 0x40047452 + PPPIOCSNPMODE = 0x4008744b + PPPIOCSPASS = 0x40107447 + PPPIOCSRASYNCMAP = 0x40047454 + PPPIOCSXASYNCMAP = 0x4020744f + PPPIOCXFERUNIT = 0x744e PRIO_PGRP = 0x1 PRIO_PROCESS = 0x0 PRIO_USER = 0x2 @@ -1687,6 +1717,7 @@ const ( SCM_TIMESTAMPNS = 0x23 SCM_TXTIME = 0x3d SCM_WIFI_STATUS = 0x29 + SC_LOG_FLUSH = 0x100000 SECCOMP_MODE_DISABLED = 0x0 SECCOMP_MODE_FILTER = 0x2 SECCOMP_MODE_STRICT = 0x1 @@ -1742,6 +1773,9 @@ const ( SIOCGMIIPHY = 0x8947 SIOCGMIIREG = 0x8948 SIOCGPGRP = 0x8904 + SIOCGPPPCSTATS = 0x89f2 + SIOCGPPPSTATS = 0x89f0 + SIOCGPPPVER = 0x89f1 SIOCGRARP = 0x8961 SIOCGSKNS = 0x894c SIOCGSTAMP = 0x8906 diff --git a/vendor/golang.org/x/sys/unix/zerrors_linux_mips.go b/vendor/golang.org/x/sys/unix/zerrors_linux_mips.go index 3c91615c8..d467d211b 100644 --- a/vendor/golang.org/x/sys/unix/zerrors_linux_mips.go +++ b/vendor/golang.org/x/sys/unix/zerrors_linux_mips.go @@ -1291,6 +1291,36 @@ const ( PERF_EVENT_IOC_SET_FILTER = 0x80042406 PERF_EVENT_IOC_SET_OUTPUT = 0x20002405 PIPEFS_MAGIC = 0x50495045 + PPPIOCATTACH = 0x8004743d + PPPIOCATTCHAN = 0x80047438 + PPPIOCCONNECT = 0x8004743a + PPPIOCDETACH = 0x8004743c + PPPIOCDISCONN = 0x20007439 + PPPIOCGASYNCMAP = 0x40047458 + PPPIOCGCHAN = 0x40047437 + PPPIOCGDEBUG = 0x40047441 + PPPIOCGFLAGS = 0x4004745a + PPPIOCGIDLE = 0x4008743f + PPPIOCGL2TPSTATS = 0x40487436 + PPPIOCGMRU = 0x40047453 + PPPIOCGNPMODE = 0xc008744c + PPPIOCGRASYNCMAP = 0x40047455 + PPPIOCGUNIT = 0x40047456 + PPPIOCGXASYNCMAP = 0x40207450 + PPPIOCNEWUNIT = 0xc004743e + PPPIOCSACTIVE = 0x80087446 + PPPIOCSASYNCMAP = 0x80047457 + PPPIOCSCOMPRESS = 0x800c744d + PPPIOCSDEBUG = 0x80047440 + PPPIOCSFLAGS = 0x80047459 + PPPIOCSMAXCID = 0x80047451 + PPPIOCSMRRU = 0x8004743b + PPPIOCSMRU = 0x80047452 + PPPIOCSNPMODE = 0x8008744b + PPPIOCSPASS = 0x80087447 + PPPIOCSRASYNCMAP = 0x80047454 + PPPIOCSXASYNCMAP = 0x8020744f + PPPIOCXFERUNIT = 0x2000744e PRIO_PGRP = 0x1 PRIO_PROCESS = 0x0 PRIO_USER = 0x2 @@ -1696,6 +1726,7 @@ const ( SCM_TIMESTAMPNS = 0x23 SCM_TXTIME = 0x3d SCM_WIFI_STATUS = 0x29 + SC_LOG_FLUSH = 0x100000 SECCOMP_MODE_DISABLED = 0x0 SECCOMP_MODE_FILTER = 0x2 SECCOMP_MODE_STRICT = 0x1 @@ -1751,6 +1782,9 @@ const ( SIOCGMIIPHY = 0x8947 SIOCGMIIREG = 0x8948 SIOCGPGRP = 0x40047309 + SIOCGPPPCSTATS = 0x89f2 + SIOCGPPPSTATS = 0x89f0 + SIOCGPPPVER = 0x89f1 SIOCGRARP = 0x8961 SIOCGSKNS = 0x894c SIOCGSTAMP = 0x8906 diff --git a/vendor/golang.org/x/sys/unix/zerrors_linux_mips64.go b/vendor/golang.org/x/sys/unix/zerrors_linux_mips64.go index e1f86c1cd..9c293ed13 100644 --- a/vendor/golang.org/x/sys/unix/zerrors_linux_mips64.go +++ b/vendor/golang.org/x/sys/unix/zerrors_linux_mips64.go @@ -1291,6 +1291,36 @@ const ( PERF_EVENT_IOC_SET_FILTER = 0x80082406 PERF_EVENT_IOC_SET_OUTPUT = 0x20002405 PIPEFS_MAGIC = 0x50495045 + PPPIOCATTACH = 0x8004743d + PPPIOCATTCHAN = 0x80047438 + PPPIOCCONNECT = 0x8004743a + PPPIOCDETACH = 0x8004743c + PPPIOCDISCONN = 0x20007439 + PPPIOCGASYNCMAP = 0x40047458 + PPPIOCGCHAN = 0x40047437 + PPPIOCGDEBUG = 0x40047441 + PPPIOCGFLAGS = 0x4004745a + PPPIOCGIDLE = 0x4010743f + PPPIOCGL2TPSTATS = 0x40487436 + PPPIOCGMRU = 0x40047453 + PPPIOCGNPMODE = 0xc008744c + PPPIOCGRASYNCMAP = 0x40047455 + PPPIOCGUNIT = 0x40047456 + PPPIOCGXASYNCMAP = 0x40207450 + PPPIOCNEWUNIT = 0xc004743e + PPPIOCSACTIVE = 0x80107446 + PPPIOCSASYNCMAP = 0x80047457 + PPPIOCSCOMPRESS = 0x8010744d + PPPIOCSDEBUG = 0x80047440 + PPPIOCSFLAGS = 0x80047459 + PPPIOCSMAXCID = 0x80047451 + PPPIOCSMRRU = 0x8004743b + PPPIOCSMRU = 0x80047452 + PPPIOCSNPMODE = 0x8008744b + PPPIOCSPASS = 0x80107447 + PPPIOCSRASYNCMAP = 0x80047454 + PPPIOCSXASYNCMAP = 0x8020744f + PPPIOCXFERUNIT = 0x2000744e PRIO_PGRP = 0x1 PRIO_PROCESS = 0x0 PRIO_USER = 0x2 @@ -1696,6 +1726,7 @@ const ( SCM_TIMESTAMPNS = 0x23 SCM_TXTIME = 0x3d SCM_WIFI_STATUS = 0x29 + SC_LOG_FLUSH = 0x100000 SECCOMP_MODE_DISABLED = 0x0 SECCOMP_MODE_FILTER = 0x2 SECCOMP_MODE_STRICT = 0x1 @@ -1751,6 +1782,9 @@ const ( SIOCGMIIPHY = 0x8947 SIOCGMIIREG = 0x8948 SIOCGPGRP = 0x40047309 + SIOCGPPPCSTATS = 0x89f2 + SIOCGPPPSTATS = 0x89f0 + SIOCGPPPVER = 0x89f1 SIOCGRARP = 0x8961 SIOCGSKNS = 0x894c SIOCGSTAMP = 0x8906 diff --git a/vendor/golang.org/x/sys/unix/zerrors_linux_mips64le.go b/vendor/golang.org/x/sys/unix/zerrors_linux_mips64le.go index d09e3b68b..e2162508c 100644 --- a/vendor/golang.org/x/sys/unix/zerrors_linux_mips64le.go +++ b/vendor/golang.org/x/sys/unix/zerrors_linux_mips64le.go @@ -1291,6 +1291,36 @@ const ( PERF_EVENT_IOC_SET_FILTER = 0x80082406 PERF_EVENT_IOC_SET_OUTPUT = 0x20002405 PIPEFS_MAGIC = 0x50495045 + PPPIOCATTACH = 0x8004743d + PPPIOCATTCHAN = 0x80047438 + PPPIOCCONNECT = 0x8004743a + PPPIOCDETACH = 0x8004743c + PPPIOCDISCONN = 0x20007439 + PPPIOCGASYNCMAP = 0x40047458 + PPPIOCGCHAN = 0x40047437 + PPPIOCGDEBUG = 0x40047441 + PPPIOCGFLAGS = 0x4004745a + PPPIOCGIDLE = 0x4010743f + PPPIOCGL2TPSTATS = 0x40487436 + PPPIOCGMRU = 0x40047453 + PPPIOCGNPMODE = 0xc008744c + PPPIOCGRASYNCMAP = 0x40047455 + PPPIOCGUNIT = 0x40047456 + PPPIOCGXASYNCMAP = 0x40207450 + PPPIOCNEWUNIT = 0xc004743e + PPPIOCSACTIVE = 0x80107446 + PPPIOCSASYNCMAP = 0x80047457 + PPPIOCSCOMPRESS = 0x8010744d + PPPIOCSDEBUG = 0x80047440 + PPPIOCSFLAGS = 0x80047459 + PPPIOCSMAXCID = 0x80047451 + PPPIOCSMRRU = 0x8004743b + PPPIOCSMRU = 0x80047452 + PPPIOCSNPMODE = 0x8008744b + PPPIOCSPASS = 0x80107447 + PPPIOCSRASYNCMAP = 0x80047454 + PPPIOCSXASYNCMAP = 0x8020744f + PPPIOCXFERUNIT = 0x2000744e PRIO_PGRP = 0x1 PRIO_PROCESS = 0x0 PRIO_USER = 0x2 @@ -1696,6 +1726,7 @@ const ( SCM_TIMESTAMPNS = 0x23 SCM_TXTIME = 0x3d SCM_WIFI_STATUS = 0x29 + SC_LOG_FLUSH = 0x100000 SECCOMP_MODE_DISABLED = 0x0 SECCOMP_MODE_FILTER = 0x2 SECCOMP_MODE_STRICT = 0x1 @@ -1751,6 +1782,9 @@ const ( SIOCGMIIPHY = 0x8947 SIOCGMIIREG = 0x8948 SIOCGPGRP = 0x40047309 + SIOCGPPPCSTATS = 0x89f2 + SIOCGPPPSTATS = 0x89f0 + SIOCGPPPVER = 0x89f1 SIOCGRARP = 0x8961 SIOCGSKNS = 0x894c SIOCGSTAMP = 0x8906 diff --git a/vendor/golang.org/x/sys/unix/zerrors_linux_mipsle.go b/vendor/golang.org/x/sys/unix/zerrors_linux_mipsle.go index f78108dfb..836c0c654 100644 --- a/vendor/golang.org/x/sys/unix/zerrors_linux_mipsle.go +++ b/vendor/golang.org/x/sys/unix/zerrors_linux_mipsle.go @@ -1291,6 +1291,36 @@ const ( PERF_EVENT_IOC_SET_FILTER = 0x80042406 PERF_EVENT_IOC_SET_OUTPUT = 0x20002405 PIPEFS_MAGIC = 0x50495045 + PPPIOCATTACH = 0x8004743d + PPPIOCATTCHAN = 0x80047438 + PPPIOCCONNECT = 0x8004743a + PPPIOCDETACH = 0x8004743c + PPPIOCDISCONN = 0x20007439 + PPPIOCGASYNCMAP = 0x40047458 + PPPIOCGCHAN = 0x40047437 + PPPIOCGDEBUG = 0x40047441 + PPPIOCGFLAGS = 0x4004745a + PPPIOCGIDLE = 0x4008743f + PPPIOCGL2TPSTATS = 0x40487436 + PPPIOCGMRU = 0x40047453 + PPPIOCGNPMODE = 0xc008744c + PPPIOCGRASYNCMAP = 0x40047455 + PPPIOCGUNIT = 0x40047456 + PPPIOCGXASYNCMAP = 0x40207450 + PPPIOCNEWUNIT = 0xc004743e + PPPIOCSACTIVE = 0x80087446 + PPPIOCSASYNCMAP = 0x80047457 + PPPIOCSCOMPRESS = 0x800c744d + PPPIOCSDEBUG = 0x80047440 + PPPIOCSFLAGS = 0x80047459 + PPPIOCSMAXCID = 0x80047451 + PPPIOCSMRRU = 0x8004743b + PPPIOCSMRU = 0x80047452 + PPPIOCSNPMODE = 0x8008744b + PPPIOCSPASS = 0x80087447 + PPPIOCSRASYNCMAP = 0x80047454 + PPPIOCSXASYNCMAP = 0x8020744f + PPPIOCXFERUNIT = 0x2000744e PRIO_PGRP = 0x1 PRIO_PROCESS = 0x0 PRIO_USER = 0x2 @@ -1696,6 +1726,7 @@ const ( SCM_TIMESTAMPNS = 0x23 SCM_TXTIME = 0x3d SCM_WIFI_STATUS = 0x29 + SC_LOG_FLUSH = 0x100000 SECCOMP_MODE_DISABLED = 0x0 SECCOMP_MODE_FILTER = 0x2 SECCOMP_MODE_STRICT = 0x1 @@ -1751,6 +1782,9 @@ const ( SIOCGMIIPHY = 0x8947 SIOCGMIIREG = 0x8948 SIOCGPGRP = 0x40047309 + SIOCGPPPCSTATS = 0x89f2 + SIOCGPPPSTATS = 0x89f0 + SIOCGPPPVER = 0x89f1 SIOCGRARP = 0x8961 SIOCGSKNS = 0x894c SIOCGSTAMP = 0x8906 diff --git a/vendor/golang.org/x/sys/unix/zerrors_linux_ppc64.go b/vendor/golang.org/x/sys/unix/zerrors_linux_ppc64.go index 8da57a99c..7ca618431 100644 --- a/vendor/golang.org/x/sys/unix/zerrors_linux_ppc64.go +++ b/vendor/golang.org/x/sys/unix/zerrors_linux_ppc64.go @@ -1292,6 +1292,36 @@ const ( PERF_EVENT_IOC_SET_FILTER = 0x80082406 PERF_EVENT_IOC_SET_OUTPUT = 0x20002405 PIPEFS_MAGIC = 0x50495045 + PPPIOCATTACH = 0x8004743d + PPPIOCATTCHAN = 0x80047438 + PPPIOCCONNECT = 0x8004743a + PPPIOCDETACH = 0x8004743c + PPPIOCDISCONN = 0x20007439 + PPPIOCGASYNCMAP = 0x40047458 + PPPIOCGCHAN = 0x40047437 + PPPIOCGDEBUG = 0x40047441 + PPPIOCGFLAGS = 0x4004745a + PPPIOCGIDLE = 0x4010743f + PPPIOCGL2TPSTATS = 0x40487436 + PPPIOCGMRU = 0x40047453 + PPPIOCGNPMODE = 0xc008744c + PPPIOCGRASYNCMAP = 0x40047455 + PPPIOCGUNIT = 0x40047456 + PPPIOCGXASYNCMAP = 0x40207450 + PPPIOCNEWUNIT = 0xc004743e + PPPIOCSACTIVE = 0x80107446 + PPPIOCSASYNCMAP = 0x80047457 + PPPIOCSCOMPRESS = 0x8010744d + PPPIOCSDEBUG = 0x80047440 + PPPIOCSFLAGS = 0x80047459 + PPPIOCSMAXCID = 0x80047451 + PPPIOCSMRRU = 0x8004743b + PPPIOCSMRU = 0x80047452 + PPPIOCSNPMODE = 0x8008744b + PPPIOCSPASS = 0x80107447 + PPPIOCSRASYNCMAP = 0x80047454 + PPPIOCSXASYNCMAP = 0x8020744f + PPPIOCXFERUNIT = 0x2000744e PRIO_PGRP = 0x1 PRIO_PROCESS = 0x0 PRIO_USER = 0x2 @@ -1752,6 +1782,7 @@ const ( SCM_TIMESTAMPNS = 0x23 SCM_TXTIME = 0x3d SCM_WIFI_STATUS = 0x29 + SC_LOG_FLUSH = 0x100000 SECCOMP_MODE_DISABLED = 0x0 SECCOMP_MODE_FILTER = 0x2 SECCOMP_MODE_STRICT = 0x1 @@ -1807,6 +1838,9 @@ const ( SIOCGMIIPHY = 0x8947 SIOCGMIIREG = 0x8948 SIOCGPGRP = 0x8904 + SIOCGPPPCSTATS = 0x89f2 + SIOCGPPPSTATS = 0x89f0 + SIOCGPPPVER = 0x89f1 SIOCGRARP = 0x8961 SIOCGSKNS = 0x894c SIOCGSTAMP = 0x8906 diff --git a/vendor/golang.org/x/sys/unix/zerrors_linux_ppc64le.go b/vendor/golang.org/x/sys/unix/zerrors_linux_ppc64le.go index 1832c0a7c..839ac214c 100644 --- a/vendor/golang.org/x/sys/unix/zerrors_linux_ppc64le.go +++ b/vendor/golang.org/x/sys/unix/zerrors_linux_ppc64le.go @@ -1292,6 +1292,36 @@ const ( PERF_EVENT_IOC_SET_FILTER = 0x80082406 PERF_EVENT_IOC_SET_OUTPUT = 0x20002405 PIPEFS_MAGIC = 0x50495045 + PPPIOCATTACH = 0x8004743d + PPPIOCATTCHAN = 0x80047438 + PPPIOCCONNECT = 0x8004743a + PPPIOCDETACH = 0x8004743c + PPPIOCDISCONN = 0x20007439 + PPPIOCGASYNCMAP = 0x40047458 + PPPIOCGCHAN = 0x40047437 + PPPIOCGDEBUG = 0x40047441 + PPPIOCGFLAGS = 0x4004745a + PPPIOCGIDLE = 0x4010743f + PPPIOCGL2TPSTATS = 0x40487436 + PPPIOCGMRU = 0x40047453 + PPPIOCGNPMODE = 0xc008744c + PPPIOCGRASYNCMAP = 0x40047455 + PPPIOCGUNIT = 0x40047456 + PPPIOCGXASYNCMAP = 0x40207450 + PPPIOCNEWUNIT = 0xc004743e + PPPIOCSACTIVE = 0x80107446 + PPPIOCSASYNCMAP = 0x80047457 + PPPIOCSCOMPRESS = 0x8010744d + PPPIOCSDEBUG = 0x80047440 + PPPIOCSFLAGS = 0x80047459 + PPPIOCSMAXCID = 0x80047451 + PPPIOCSMRRU = 0x8004743b + PPPIOCSMRU = 0x80047452 + PPPIOCSNPMODE = 0x8008744b + PPPIOCSPASS = 0x80107447 + PPPIOCSRASYNCMAP = 0x80047454 + PPPIOCSXASYNCMAP = 0x8020744f + PPPIOCXFERUNIT = 0x2000744e PRIO_PGRP = 0x1 PRIO_PROCESS = 0x0 PRIO_USER = 0x2 @@ -1752,6 +1782,7 @@ const ( SCM_TIMESTAMPNS = 0x23 SCM_TXTIME = 0x3d SCM_WIFI_STATUS = 0x29 + SC_LOG_FLUSH = 0x100000 SECCOMP_MODE_DISABLED = 0x0 SECCOMP_MODE_FILTER = 0x2 SECCOMP_MODE_STRICT = 0x1 @@ -1807,6 +1838,9 @@ const ( SIOCGMIIPHY = 0x8947 SIOCGMIIREG = 0x8948 SIOCGPGRP = 0x8904 + SIOCGPPPCSTATS = 0x89f2 + SIOCGPPPSTATS = 0x89f0 + SIOCGPPPVER = 0x89f1 SIOCGRARP = 0x8961 SIOCGSKNS = 0x894c SIOCGSTAMP = 0x8906 diff --git a/vendor/golang.org/x/sys/unix/zerrors_linux_riscv64.go b/vendor/golang.org/x/sys/unix/zerrors_linux_riscv64.go index c6bd4efff..a747aa1b7 100644 --- a/vendor/golang.org/x/sys/unix/zerrors_linux_riscv64.go +++ b/vendor/golang.org/x/sys/unix/zerrors_linux_riscv64.go @@ -1291,6 +1291,36 @@ const ( PERF_EVENT_IOC_SET_FILTER = 0x40082406 PERF_EVENT_IOC_SET_OUTPUT = 0x2405 PIPEFS_MAGIC = 0x50495045 + PPPIOCATTACH = 0x4004743d + PPPIOCATTCHAN = 0x40047438 + PPPIOCCONNECT = 0x4004743a + PPPIOCDETACH = 0x4004743c + PPPIOCDISCONN = 0x7439 + PPPIOCGASYNCMAP = 0x80047458 + PPPIOCGCHAN = 0x80047437 + PPPIOCGDEBUG = 0x80047441 + PPPIOCGFLAGS = 0x8004745a + PPPIOCGIDLE = 0x8010743f + PPPIOCGL2TPSTATS = 0x80487436 + PPPIOCGMRU = 0x80047453 + PPPIOCGNPMODE = 0xc008744c + PPPIOCGRASYNCMAP = 0x80047455 + PPPIOCGUNIT = 0x80047456 + PPPIOCGXASYNCMAP = 0x80207450 + PPPIOCNEWUNIT = 0xc004743e + PPPIOCSACTIVE = 0x40107446 + PPPIOCSASYNCMAP = 0x40047457 + PPPIOCSCOMPRESS = 0x4010744d + PPPIOCSDEBUG = 0x40047440 + PPPIOCSFLAGS = 0x40047459 + PPPIOCSMAXCID = 0x40047451 + PPPIOCSMRRU = 0x4004743b + PPPIOCSMRU = 0x40047452 + PPPIOCSNPMODE = 0x4008744b + PPPIOCSPASS = 0x40107447 + PPPIOCSRASYNCMAP = 0x40047454 + PPPIOCSXASYNCMAP = 0x4020744f + PPPIOCXFERUNIT = 0x744e PRIO_PGRP = 0x1 PRIO_PROCESS = 0x0 PRIO_USER = 0x2 @@ -1684,6 +1714,7 @@ const ( SCM_TIMESTAMPNS = 0x23 SCM_TXTIME = 0x3d SCM_WIFI_STATUS = 0x29 + SC_LOG_FLUSH = 0x100000 SECCOMP_MODE_DISABLED = 0x0 SECCOMP_MODE_FILTER = 0x2 SECCOMP_MODE_STRICT = 0x1 @@ -1739,6 +1770,9 @@ const ( SIOCGMIIPHY = 0x8947 SIOCGMIIREG = 0x8948 SIOCGPGRP = 0x8904 + SIOCGPPPCSTATS = 0x89f2 + SIOCGPPPSTATS = 0x89f0 + SIOCGPPPVER = 0x89f1 SIOCGRARP = 0x8961 SIOCGSKNS = 0x894c SIOCGSTAMP = 0x8906 diff --git a/vendor/golang.org/x/sys/unix/zerrors_linux_s390x.go b/vendor/golang.org/x/sys/unix/zerrors_linux_s390x.go index 8cdf353dc..96aff5083 100644 --- a/vendor/golang.org/x/sys/unix/zerrors_linux_s390x.go +++ b/vendor/golang.org/x/sys/unix/zerrors_linux_s390x.go @@ -1291,6 +1291,36 @@ const ( PERF_EVENT_IOC_SET_FILTER = 0x40082406 PERF_EVENT_IOC_SET_OUTPUT = 0x2405 PIPEFS_MAGIC = 0x50495045 + PPPIOCATTACH = 0x4004743d + PPPIOCATTCHAN = 0x40047438 + PPPIOCCONNECT = 0x4004743a + PPPIOCDETACH = 0x4004743c + PPPIOCDISCONN = 0x7439 + PPPIOCGASYNCMAP = 0x80047458 + PPPIOCGCHAN = 0x80047437 + PPPIOCGDEBUG = 0x80047441 + PPPIOCGFLAGS = 0x8004745a + PPPIOCGIDLE = 0x8010743f + PPPIOCGL2TPSTATS = 0x80487436 + PPPIOCGMRU = 0x80047453 + PPPIOCGNPMODE = 0xc008744c + PPPIOCGRASYNCMAP = 0x80047455 + PPPIOCGUNIT = 0x80047456 + PPPIOCGXASYNCMAP = 0x80207450 + PPPIOCNEWUNIT = 0xc004743e + PPPIOCSACTIVE = 0x40107446 + PPPIOCSASYNCMAP = 0x40047457 + PPPIOCSCOMPRESS = 0x4010744d + PPPIOCSDEBUG = 0x40047440 + PPPIOCSFLAGS = 0x40047459 + PPPIOCSMAXCID = 0x40047451 + PPPIOCSMRRU = 0x4004743b + PPPIOCSMRU = 0x40047452 + PPPIOCSNPMODE = 0x4008744b + PPPIOCSPASS = 0x40107447 + PPPIOCSRASYNCMAP = 0x40047454 + PPPIOCSXASYNCMAP = 0x4020744f + PPPIOCXFERUNIT = 0x744e PRIO_PGRP = 0x1 PRIO_PROCESS = 0x0 PRIO_USER = 0x2 @@ -1757,6 +1787,7 @@ const ( SCM_TIMESTAMPNS = 0x23 SCM_TXTIME = 0x3d SCM_WIFI_STATUS = 0x29 + SC_LOG_FLUSH = 0x100000 SECCOMP_MODE_DISABLED = 0x0 SECCOMP_MODE_FILTER = 0x2 SECCOMP_MODE_STRICT = 0x1 @@ -1812,6 +1843,9 @@ const ( SIOCGMIIPHY = 0x8947 SIOCGMIIREG = 0x8948 SIOCGPGRP = 0x8904 + SIOCGPPPCSTATS = 0x89f2 + SIOCGPPPSTATS = 0x89f0 + SIOCGPPPVER = 0x89f1 SIOCGRARP = 0x8961 SIOCGSKNS = 0x894c SIOCGSTAMP = 0x8906 From b496d9aafd6c0c2901d764b50511be8cfa3431cf Mon Sep 17 00:00:00 2001 From: Qi Xiao Date: Tue, 20 Nov 2018 14:50:39 +0800 Subject: [PATCH 06/24] Support mysql prepare/exec feature --- cmd/cql-mysql-adapter/cursor.go | 192 ++++++++++++++++++++++++++++--- cmd/cql-secure-gateway/cursor.go | 192 ++++++++++++++++++++++++++++--- 2 files changed, 356 insertions(+), 28 deletions(-) diff --git a/cmd/cql-mysql-adapter/cursor.go b/cmd/cql-mysql-adapter/cursor.go index 7583c1996..618631fe7 100644 --- a/cmd/cql-mysql-adapter/cursor.go +++ b/cmd/cql-mysql-adapter/cursor.go @@ -19,6 +19,7 @@ package main import ( "database/sql" "fmt" + "io" "path/filepath" "regexp" "strings" @@ -26,9 +27,19 @@ import ( "github.com/CovenantSQL/CovenantSQL/client" "github.com/CovenantSQL/CovenantSQL/utils/log" + "github.com/CovenantSQL/sqlparser" my "github.com/siddontang/go-mysql/mysql" ) +// preset column counts for special statements +const ( + columnCountShowCreateTable = 1 // sql + columnCountShowTableInfo = 6 // cid|name|type|notnull|dflt_value|pk + columnCountShowIndex = 1 // name + columnCountShowTables = 1 // name + columnCountExplain = 8 // addr|opcode|p1|p2|p3|p4|p5|comment +) + var ( dbIDRegex = regexp.MustCompile("^[a-zA-Z0-9_\\.]+$") specialSelectQuery = regexp.MustCompile("^(?i)SELECT\\s+(DATABASE|USER)\\(\\)\\s*;?\\s*$") @@ -146,9 +157,11 @@ func (c *Cursor) handleSpecialQuery(query string) (r *my.Result, processed bool, processed = true } else if emptyResultWithResultSetQuery.MatchString(query) { // send empty result include non-nil result set // return empty result with empty result set - var resultSet *my.Resultset - var columns []string - var row []interface{} + var ( + resultSet *my.Resultset + columns []string + row []interface{} + ) for k, v := range mysqlServerVariables { if strings.Contains(query, k) { @@ -362,12 +375,15 @@ func (c *Cursor) HandleFieldList(table string, fieldWildcard string) (fields []* fieldGlob = strings.NewReplacer("_", "?", "%", "*").Replace(fieldWildcard) } - var cid, defaultValue interface{} - var columnName, typeString string - var isNotNull, isPK bool + var ( + cid, defaultValue interface{} + columnName, typeString string + isNotNull, isPK bool + ) for columns.Next() { if err = columns.Scan(&cid, &columnName, &typeString, &isNotNull, &defaultValue, &isPK); err != nil { + err = my.NewError(my.ER_UNKNOWN_ERROR, err.Error()) return } @@ -407,20 +423,168 @@ func (c *Cursor) HandleFieldList(table string, fieldWildcard string) (fields []* // HandleStmtPrepare handle COM_STMT_PREPARE, params is the param number for this statement, columns is the column number // context will be used later for statement execute. func (c *Cursor) HandleStmtPrepare(query string) (params int, columns int, context interface{}, err error) { - // TODO(xq26144), not implemented - // According to the libmysql standard: https://github.com/mysql/mysql-server/blob/8.0/libmysql/libmysql.cc#L1599 - // the COM_STMT_PREPARE should return the correct bind parameter count (which can be implemented by newly created parser) - // and should return the correct number of return fields (which can not be implemented right now with new query plan logic embedded) + // for special queries + var processed bool + var r *my.Result + + log.WithField("query", query).Info("received query") + + if r, processed, err = c.handleSpecialQuery(query); processed { + // set context as result + context = r + return + } + + var conn *sql.DB + + if conn, err = c.ensureDatabase(); err != nil { + return + } + + // tokenize commands + var ( + tokenizer = sqlparser.NewStringTokenizer(query) + stmt sqlparser.Statement + lastPos int + singleQuery string + ) + + for { + stmt, err = sqlparser.ParseNext(tokenizer) + + // parse statement failed + if err != nil && err != io.EOF { + return + } + + if err == io.EOF { + err = nil + break + } + + if lastPos != 0 { + // multiple queries + err = my.NewError(my.ER_SYNTAX_ERROR, "could not prepare multiple statements") + return + } + + singleQuery = query[lastPos : tokenizer.Position-1] + lastPos = tokenizer.Position + 1 + } + + if strings.TrimSpace(singleQuery) == "" { + // empty query + err = my.NewError(my.ER_SYNTAX_ERROR, "query is empty") + return + } + + // calculate param count for special statements + switch s := stmt.(type) { + case *sqlparser.Show: + params = 0 + switch s.Type { + case "table": + if s.ShowCreate { + columns = columnCountShowCreateTable + } else { + columns = columnCountShowTableInfo + } + case "index": + columns = columnCountShowIndex + case "tables": + columns = columnCountShowTables + default: + err = my.NewError(my.ER_UNKNOWN_ERROR, "unknown query") + } + return + case *sqlparser.Explain: + params = 0 + columns = columnCountExplain + return + } + + // HACK(xq262144), this feature is supported by sqlite only, replace with query plan analysis for mysql engine + // normal statement, using explain to calculate param and result column count + // using opcode descriptions in https://www.sqlite.org/opcode.html to resolve + var rows *sql.Rows + if rows, err = conn.Query("EXPLAIN " + query); err != nil { + err = my.NewError(my.ER_UNKNOWN_ERROR, err.Error()) + return + } + + defer rows.Close() + + var ( + opcode string + p1, p2 int + addr, p3, p4, p5, comment interface{} // not care about this fields + ) + + for rows.Next() { + if err = rows.Scan(&addr, &opcode, &p1, &p2, &p3, &p4, &p5, &comment); err != nil { + err = my.NewError(my.ER_UNKNOWN_ERROR, err.Error()) + return + } + + // p1 for variables is + if opcode == "Variable" && p1 > params { + params = p1 + } + + // p2 is the result column count + if opcode == "ResultRow" { + columns = p2 + } + } - err = my.NewError(my.ER_NOT_SUPPORTED_YET, "stmt prepare is not supported yet") return } // HandleStmtExecute handle COM_STMT_EXECUTE, context is the previous one set in prepare // query is the statement prepare query, and args is the params for this statement. -func (c *Cursor) HandleStmtExecute(context interface{}, query string, args []interface{}) (result *my.Result, err error) { - // same to COM_STMT_PREPARE - err = my.NewError(my.ER_NOT_SUPPORTED_YET, "stmt execute is not supported yet") +func (c *Cursor) HandleStmtExecute(context interface{}, query string, args []interface{}) (r *my.Result, err error) { + // special query + if context != nil { + var ok bool + if r, ok = context.(*my.Result); ok { + return + } + } + + var conn *sql.DB + + if conn, err = c.ensureDatabase(); err != nil { + return + } + + // normal query + if readQuery.MatchString(query) { + var rows *sql.Rows + if rows, err = conn.Query(query, args...); err != nil { + err = my.NewError(my.ER_UNKNOWN_ERROR, err.Error()) + return + } + + // build result set + return c.buildResultSet(rows) + } + + var result sql.Result + if result, err = conn.Exec(query, args...); err != nil { + err = my.NewError(my.ER_UNKNOWN_ERROR, err.Error()) + return + } + + lastInsertID, _ := result.LastInsertId() + affectedRows, _ := result.RowsAffected() + + r = &my.Result{ + Status: 0, + InsertId: uint64(lastInsertID), + AffectedRows: uint64(affectedRows), + Resultset: nil, + } + return } diff --git a/cmd/cql-secure-gateway/cursor.go b/cmd/cql-secure-gateway/cursor.go index 950b0a008..f3c33f813 100644 --- a/cmd/cql-secure-gateway/cursor.go +++ b/cmd/cql-secure-gateway/cursor.go @@ -19,6 +19,7 @@ package main import ( "database/sql" "fmt" + "io" "path/filepath" "regexp" "strings" @@ -26,9 +27,19 @@ import ( "github.com/CovenantSQL/CovenantSQL/client" "github.com/CovenantSQL/CovenantSQL/utils/log" + "github.com/CovenantSQL/sqlparser" my "github.com/siddontang/go-mysql/mysql" ) +// preset column counts for special statements +const ( + columnCountShowCreateTable = 1 // sql + columnCountShowTableInfo = 6 // cid|name|type|notnull|dflt_value|pk + columnCountShowIndex = 1 // name + columnCountShowTables = 1 // name + columnCountExplain = 8 // addr|opcode|p1|p2|p3|p4|p5|comment +) + var ( dbIDRegex = regexp.MustCompile("^[a-zA-Z0-9_\\.]+$") specialSelectQuery = regexp.MustCompile("^(?i)SELECT\\s+(DATABASE|USER)\\(\\)\\s*;?\\s*$") @@ -147,9 +158,11 @@ func (c *Cursor) handleSpecialQuery(query string) (r *my.Result, processed bool, processed = true } else if emptyResultWithResultSetQuery.MatchString(query) { // send empty result include non-nil result set // return empty result with empty result set - var resultSet *my.Resultset - var columns []string - var row []interface{} + var ( + resultSet *my.Resultset + columns []string + row []interface{} + ) for k, v := range mysqlServerVariables { if strings.Contains(query, k) { @@ -363,12 +376,15 @@ func (c *Cursor) HandleFieldList(table string, fieldWildcard string) (fields []* fieldGlob = strings.NewReplacer("_", "?", "%", "*").Replace(fieldWildcard) } - var cid, defaultValue interface{} - var columnName, typeString string - var isNotNull, isPK bool + var ( + cid, defaultValue interface{} + columnName, typeString string + isNotNull, isPK bool + ) for columns.Next() { if err = columns.Scan(&cid, &columnName, &typeString, &isNotNull, &defaultValue, &isPK); err != nil { + err = my.NewError(my.ER_UNKNOWN_ERROR, err.Error()) return } @@ -408,20 +424,168 @@ func (c *Cursor) HandleFieldList(table string, fieldWildcard string) (fields []* // HandleStmtPrepare handle COM_STMT_PREPARE, params is the param number for this statement, columns is the column number // context will be used later for statement execute. func (c *Cursor) HandleStmtPrepare(query string) (params int, columns int, context interface{}, err error) { - // TODO(xq26144), not implemented - // According to the libmysql standard: https://github.com/mysql/mysql-server/blob/8.0/libmysql/libmysql.cc#L1599 - // the COM_STMT_PREPARE should return the correct bind parameter count (which can be implemented by newly created parser) - // and should return the correct number of return fields (which can not be implemented right now with new query plan logic embedded) + // for special queries + var processed bool + var r *my.Result + + log.WithField("query", query).Info("received query") + + if r, processed, err = c.handleSpecialQuery(query); processed { + // set context as result + context = r + return + } + + var conn *sql.DB + + if conn, err = c.ensureDatabase(); err != nil { + return + } + + // tokenize commands + var ( + tokenizer = sqlparser.NewStringTokenizer(query) + stmt sqlparser.Statement + lastPos int + singleQuery string + ) + + for { + stmt, err = sqlparser.ParseNext(tokenizer) + + // parse statement failed + if err != nil && err != io.EOF { + return + } + + if err == io.EOF { + err = nil + break + } + + if lastPos != 0 { + // multiple queries + err = my.NewError(my.ER_SYNTAX_ERROR, "could not prepare multiple statements") + return + } + + singleQuery = query[lastPos : tokenizer.Position-1] + lastPos = tokenizer.Position + 1 + } + + if strings.TrimSpace(singleQuery) == "" { + // empty query + err = my.NewError(my.ER_SYNTAX_ERROR, "query is empty") + return + } + + // calculate param count for special statements + switch s := stmt.(type) { + case *sqlparser.Show: + params = 0 + switch s.Type { + case "table": + if s.ShowCreate { + columns = columnCountShowCreateTable + } else { + columns = columnCountShowTableInfo + } + case "index": + columns = columnCountShowIndex + case "tables": + columns = columnCountShowTables + default: + err = my.NewError(my.ER_UNKNOWN_ERROR, "unknown query") + } + return + case *sqlparser.Explain: + params = 0 + columns = columnCountExplain + return + } + + // HACK(xq262144), this feature is supported by sqlite only, replace with query plan analysis for mysql engine + // normal statement, using explain to calculate param and result column count + // using opcode descriptions in https://www.sqlite.org/opcode.html to resolve + var rows *sql.Rows + if rows, err = conn.Query("EXPLAIN " + query); err != nil { + err = my.NewError(my.ER_UNKNOWN_ERROR, err.Error()) + return + } + + defer rows.Close() + + var ( + opcode string + p1, p2 int + addr, p3, p4, p5, comment interface{} // not care about this fields + ) + + for rows.Next() { + if err = rows.Scan(&addr, &opcode, &p1, &p2, &p3, &p4, &p5, &comment); err != nil { + err = my.NewError(my.ER_UNKNOWN_ERROR, err.Error()) + return + } + + // p1 for variables is + if opcode == "Variable" && p1 > params { + params = p1 + } + + // p2 is the result column count + if opcode == "ResultRow" { + columns = p2 + } + } - err = my.NewError(my.ER_NOT_SUPPORTED_YET, "stmt prepare is not supported yet") return } // HandleStmtExecute handle COM_STMT_EXECUTE, context is the previous one set in prepare // query is the statement prepare query, and args is the params for this statement. -func (c *Cursor) HandleStmtExecute(context interface{}, query string, args []interface{}) (result *my.Result, err error) { - // same to COM_STMT_PREPARE - err = my.NewError(my.ER_NOT_SUPPORTED_YET, "stmt execute is not supported yet") +func (c *Cursor) HandleStmtExecute(context interface{}, query string, args []interface{}) (r *my.Result, err error) { + // special query + if context != nil { + var ok bool + if r, ok = context.(*my.Result); ok { + return + } + } + + var conn *sql.DB + + if conn, err = c.ensureDatabase(); err != nil { + return + } + + // normal query + if readQuery.MatchString(query) { + var rows *sql.Rows + if rows, err = conn.Query(query, args...); err != nil { + err = my.NewError(my.ER_UNKNOWN_ERROR, err.Error()) + return + } + + // build result set + return c.buildResultSet(rows) + } + + var result sql.Result + if result, err = conn.Exec(query, args...); err != nil { + err = my.NewError(my.ER_UNKNOWN_ERROR, err.Error()) + return + } + + lastInsertID, _ := result.LastInsertId() + affectedRows, _ := result.RowsAffected() + + r = &my.Result{ + Status: 0, + InsertId: uint64(lastInsertID), + AffectedRows: uint64(affectedRows), + Resultset: nil, + } + return } From 64c08d914373afc9939e9ee657ef9aa80dd9617a Mon Sep 17 00:00:00 2001 From: Qi Xiao Date: Tue, 20 Nov 2018 14:51:36 +0800 Subject: [PATCH 07/24] Update deps Add missing files in vendor directory --- vendor/bazil.org/fuse/.gitignore | 11 + .../CovenantSQL/go-sqlite3-encrypt/.gitignore | 16 ++ .../go-sqlite3-encrypt/.travis.yml | 41 +++ .../CovenantSQL/sqlparser/.gitignore | 22 ++ .../CovenantSQL/sqlparser/.travis.yml | 17 ++ .../github.com/CovenantSQL/xurls/.gitignore | 3 + .../github.com/CovenantSQL/xurls/.travis.yml | 7 + vendor/github.com/Knetic/govaluate/.gitignore | 28 ++ .../github.com/Knetic/govaluate/.travis.yml | 10 + .../github.com/alecthomas/chroma/.gitignore | 18 ++ .../github.com/alecthomas/chroma/.travis.yml | 5 + vendor/github.com/beevik/ntp/.travis.yml | 15 + vendor/github.com/casbin/casbin/.gitignore | 27 ++ vendor/github.com/casbin/casbin/.travis.yml | 12 + .../github.com/casbin/casbin/config/config.go | 266 ++++++++++++++++++ vendor/github.com/coreos/bbolt/.gitignore | 4 + .../cyberdelia/go-metrics-graphite/.gitignore | 1 + .../go-metrics-graphite/.travis.yml | 3 + .../danwakefield/fnmatch/.gitignore | 24 ++ vendor/github.com/dlclark/regexp2/.DS_Store | Bin 0 -> 6148 bytes vendor/github.com/dlclark/regexp2/.gitignore | 25 ++ vendor/github.com/dlclark/regexp2/.travis.yml | 5 + .../github.com/fortytw2/leaktest/.travis.yml | 16 ++ vendor/github.com/gohxs/readline/.travis.yml | 9 + vendor/github.com/golang/snappy/.gitignore | 16 ++ vendor/github.com/gorilla/context/.travis.yml | 19 ++ .../github.com/gorilla/handlers/.travis.yml | 20 ++ vendor/github.com/gorilla/mux/.travis.yml | 23 ++ vendor/github.com/jmoiron/jsonq/.gitignore | 1 + vendor/github.com/juju/errors/.gitignore | 23 ++ vendor/github.com/knq/snaker/.travis.yml | 10 + vendor/github.com/lufia/iostat/.gitignore | 14 + vendor/github.com/mattn/go-isatty/.travis.yml | 13 + .../github.com/mattn/go-runewidth/.travis.yml | 8 + .../pbutil/.gitignore | 1 + vendor/github.com/miekg/dns/.gitignore | 4 + vendor/github.com/miekg/dns/.travis.yml | 18 ++ .../github.com/minio/blake2b-simd/.gitignore | 1 + .../github.com/minio/blake2b-simd/.travis.yml | 21 ++ vendor/github.com/pkg/errors/.gitignore | 24 ++ vendor/github.com/pkg/errors/.travis.yml | 11 + .../client_golang/prometheus/.gitignore | 1 + .../github.com/prometheus/procfs/.gitignore | 1 + .../github.com/rcrowley/go-metrics/.gitignore | 9 + .../rcrowley/go-metrics/.travis.yml | 17 ++ vendor/github.com/satori/go.uuid/.travis.yml | 21 ++ vendor/github.com/ulule/deepcopier/.gitignore | 24 ++ .../github.com/ulule/deepcopier/.travis.yml | 10 + vendor/github.com/xo/dburl/.gitignore | 2 + vendor/github.com/xo/dburl/.travis.yml | 10 + vendor/github.com/xo/tblfmt/.gitignore | 2 + vendor/github.com/xo/tblfmt/.travis.yml | 7 + vendor/github.com/xo/terminfo/.gitignore | 9 + vendor/github.com/xo/terminfo/.travis.yml | 15 + vendor/github.com/xtaci/smux/.gitignore | 24 ++ vendor/github.com/xtaci/smux/.travis.yml | 15 + vendor/gopkg.in/yaml.v2/.travis.yml | 12 + 57 files changed, 991 insertions(+) create mode 100644 vendor/bazil.org/fuse/.gitignore create mode 100644 vendor/github.com/CovenantSQL/go-sqlite3-encrypt/.gitignore create mode 100644 vendor/github.com/CovenantSQL/go-sqlite3-encrypt/.travis.yml create mode 100644 vendor/github.com/CovenantSQL/sqlparser/.gitignore create mode 100644 vendor/github.com/CovenantSQL/sqlparser/.travis.yml create mode 100644 vendor/github.com/CovenantSQL/xurls/.gitignore create mode 100644 vendor/github.com/CovenantSQL/xurls/.travis.yml create mode 100644 vendor/github.com/Knetic/govaluate/.gitignore create mode 100644 vendor/github.com/Knetic/govaluate/.travis.yml create mode 100644 vendor/github.com/alecthomas/chroma/.gitignore create mode 100644 vendor/github.com/alecthomas/chroma/.travis.yml create mode 100644 vendor/github.com/beevik/ntp/.travis.yml create mode 100644 vendor/github.com/casbin/casbin/.gitignore create mode 100644 vendor/github.com/casbin/casbin/.travis.yml create mode 100644 vendor/github.com/casbin/casbin/config/config.go create mode 100644 vendor/github.com/coreos/bbolt/.gitignore create mode 100644 vendor/github.com/cyberdelia/go-metrics-graphite/.gitignore create mode 100644 vendor/github.com/cyberdelia/go-metrics-graphite/.travis.yml create mode 100644 vendor/github.com/danwakefield/fnmatch/.gitignore create mode 100644 vendor/github.com/dlclark/regexp2/.DS_Store create mode 100644 vendor/github.com/dlclark/regexp2/.gitignore create mode 100644 vendor/github.com/dlclark/regexp2/.travis.yml create mode 100644 vendor/github.com/fortytw2/leaktest/.travis.yml create mode 100644 vendor/github.com/gohxs/readline/.travis.yml create mode 100644 vendor/github.com/golang/snappy/.gitignore create mode 100644 vendor/github.com/gorilla/context/.travis.yml create mode 100644 vendor/github.com/gorilla/handlers/.travis.yml create mode 100644 vendor/github.com/gorilla/mux/.travis.yml create mode 100644 vendor/github.com/jmoiron/jsonq/.gitignore create mode 100644 vendor/github.com/juju/errors/.gitignore create mode 100644 vendor/github.com/knq/snaker/.travis.yml create mode 100644 vendor/github.com/lufia/iostat/.gitignore create mode 100644 vendor/github.com/mattn/go-isatty/.travis.yml create mode 100644 vendor/github.com/mattn/go-runewidth/.travis.yml create mode 100644 vendor/github.com/matttproud/golang_protobuf_extensions/pbutil/.gitignore create mode 100644 vendor/github.com/miekg/dns/.gitignore create mode 100644 vendor/github.com/miekg/dns/.travis.yml create mode 100644 vendor/github.com/minio/blake2b-simd/.gitignore create mode 100644 vendor/github.com/minio/blake2b-simd/.travis.yml create mode 100644 vendor/github.com/pkg/errors/.gitignore create mode 100644 vendor/github.com/pkg/errors/.travis.yml create mode 100644 vendor/github.com/prometheus/client_golang/prometheus/.gitignore create mode 100644 vendor/github.com/prometheus/procfs/.gitignore create mode 100644 vendor/github.com/rcrowley/go-metrics/.gitignore create mode 100644 vendor/github.com/rcrowley/go-metrics/.travis.yml create mode 100644 vendor/github.com/satori/go.uuid/.travis.yml create mode 100644 vendor/github.com/ulule/deepcopier/.gitignore create mode 100644 vendor/github.com/ulule/deepcopier/.travis.yml create mode 100644 vendor/github.com/xo/dburl/.gitignore create mode 100644 vendor/github.com/xo/dburl/.travis.yml create mode 100644 vendor/github.com/xo/tblfmt/.gitignore create mode 100644 vendor/github.com/xo/tblfmt/.travis.yml create mode 100644 vendor/github.com/xo/terminfo/.gitignore create mode 100644 vendor/github.com/xo/terminfo/.travis.yml create mode 100644 vendor/github.com/xtaci/smux/.gitignore create mode 100644 vendor/github.com/xtaci/smux/.travis.yml create mode 100644 vendor/gopkg.in/yaml.v2/.travis.yml diff --git a/vendor/bazil.org/fuse/.gitignore b/vendor/bazil.org/fuse/.gitignore new file mode 100644 index 000000000..53589948c --- /dev/null +++ b/vendor/bazil.org/fuse/.gitignore @@ -0,0 +1,11 @@ +*~ +.#* +## the next line needs to start with a backslash to avoid looking like +## a comment +\#*# +.*.swp + +*.test + +/clockfs +/hellofs diff --git a/vendor/github.com/CovenantSQL/go-sqlite3-encrypt/.gitignore b/vendor/github.com/CovenantSQL/go-sqlite3-encrypt/.gitignore new file mode 100644 index 000000000..4492adfc4 --- /dev/null +++ b/vendor/github.com/CovenantSQL/go-sqlite3-encrypt/.gitignore @@ -0,0 +1,16 @@ +*.db +*.exe +*.dll +*.o + +.idea + +# VSCode +.vscode + +# Exclude from upgrade +upgrade/*.c +upgrade/*.h + +# Exclude upgrade binary +upgrade/upgrade diff --git a/vendor/github.com/CovenantSQL/go-sqlite3-encrypt/.travis.yml b/vendor/github.com/CovenantSQL/go-sqlite3-encrypt/.travis.yml new file mode 100644 index 000000000..a21c8170e --- /dev/null +++ b/vendor/github.com/CovenantSQL/go-sqlite3-encrypt/.travis.yml @@ -0,0 +1,41 @@ +language: go + +os: + - linux + - osx + +addons: + apt: + update: true + +env: + matrix: + - GOTAGS= + - GOTAGS=libsqlite3 + - GOTAGS="sqlite_allow_uri_authority sqlite_app_armor sqlite_foreign_keys sqlite_fts5 sqlite_icu sqlite_introspect sqlite_json sqlite_secure_delete sqlite_see sqlite_stat4 sqlite_trace sqlite_userauth sqlite_vacuum_incr sqlite_vtable" + - GOTAGS=sqlite_vacuum_full + +go: + - 1.9.x + - 1.10.x + +before_install: + - | + if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then + brew update + brew upgrade icu4c + fi + - | + go get github.com/smartystreets/goconvey + if [[ "${GOOS}" != "windows" ]]; then + go get github.com/mattn/goveralls + go get golang.org/x/tools/cmd/cover + fi + +script: + - GOOS=$(go env GOOS) GOARCH=$(go env GOARCH) go build -v -tags "${GOTAGS}" . + - | + if [[ "${GOOS}" != "windows" ]]; then + $HOME/gopath/bin/goveralls -repotoken 3qJVUE0iQwqnCbmNcDsjYu1nh4J4KIFXx + go test -race -v . -tags "${GOTAGS}" + fi diff --git a/vendor/github.com/CovenantSQL/sqlparser/.gitignore b/vendor/github.com/CovenantSQL/sqlparser/.gitignore new file mode 100644 index 000000000..b5f55c771 --- /dev/null +++ b/vendor/github.com/CovenantSQL/sqlparser/.gitignore @@ -0,0 +1,22 @@ +.travis.yml +coverage.txt +analysisVendor.png +.DS_Store +y.output + +# Binaries for programs and plugins +bin/ +*.exe +*.dll +*.so +*.dylib + +# Test binary, build with `go test -c` +*.test + +# Output of the go coverage tool, specifically when used with LiteIDE +*.out + +# Project-local glide cache, RE: https://github.com/Masterminds/glide/issues/736 +.glide/ +.idea/ diff --git a/vendor/github.com/CovenantSQL/sqlparser/.travis.yml b/vendor/github.com/CovenantSQL/sqlparser/.travis.yml new file mode 100644 index 000000000..43c9bafa9 --- /dev/null +++ b/vendor/github.com/CovenantSQL/sqlparser/.travis.yml @@ -0,0 +1,17 @@ +dist: trusty +sudo: false + +language: go +go: + - 1.6.x + - 1.7.x + - 1.8.x + - 1.9.x + +before_install: + - go get github.com/mattn/goveralls + - go get golang.org/x/tools/cmd/cover + - go install golang.org/x/tools/cmd/goyacc + +script: + - travis_retry $HOME/gopath/bin/goveralls -service=travis-ci diff --git a/vendor/github.com/CovenantSQL/xurls/.gitignore b/vendor/github.com/CovenantSQL/xurls/.gitignore new file mode 100644 index 000000000..663c8cb54 --- /dev/null +++ b/vendor/github.com/CovenantSQL/xurls/.gitignore @@ -0,0 +1,3 @@ +cmd/xurls/xurls +generate/tldsgen/tldsgen +generate/regexgen/regexgen diff --git a/vendor/github.com/CovenantSQL/xurls/.travis.yml b/vendor/github.com/CovenantSQL/xurls/.travis.yml new file mode 100644 index 000000000..17556296e --- /dev/null +++ b/vendor/github.com/CovenantSQL/xurls/.travis.yml @@ -0,0 +1,7 @@ +language: go + +go_import_path: mvdan.cc/xurls + +go: + - 1.9.x + - 1.10.x diff --git a/vendor/github.com/Knetic/govaluate/.gitignore b/vendor/github.com/Knetic/govaluate/.gitignore new file mode 100644 index 000000000..da210fb31 --- /dev/null +++ b/vendor/github.com/Knetic/govaluate/.gitignore @@ -0,0 +1,28 @@ +# 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 +coverage.out + +manual_test.go +*.out +*.err diff --git a/vendor/github.com/Knetic/govaluate/.travis.yml b/vendor/github.com/Knetic/govaluate/.travis.yml new file mode 100644 index 000000000..35ae404ab --- /dev/null +++ b/vendor/github.com/Knetic/govaluate/.travis.yml @@ -0,0 +1,10 @@ +language: go + +script: ./test.sh + +go: + - 1.2 + - 1.3 + - 1.4 + - 1.5 + - 1.6 diff --git a/vendor/github.com/alecthomas/chroma/.gitignore b/vendor/github.com/alecthomas/chroma/.gitignore new file mode 100644 index 000000000..94dc6b97c --- /dev/null +++ b/vendor/github.com/alecthomas/chroma/.gitignore @@ -0,0 +1,18 @@ +# Binaries for programs and plugins +*.exe +*.dll +*.so +*.dylib +/cmd/chroma/chroma + +# Test binary, build with `go test -c` +*.test + +# Output of the go coverage tool, specifically when used with LiteIDE +*.out + +# Project-local glide cache, RE: https://github.com/Masterminds/glide/issues/736 +.glide/ + +_models/ + diff --git a/vendor/github.com/alecthomas/chroma/.travis.yml b/vendor/github.com/alecthomas/chroma/.travis.yml new file mode 100644 index 000000000..3e27eb3e7 --- /dev/null +++ b/vendor/github.com/alecthomas/chroma/.travis.yml @@ -0,0 +1,5 @@ +sudo: false +language: go +after_success: + go get github.com/goreleaser/goreleaser && goreleaser + diff --git a/vendor/github.com/beevik/ntp/.travis.yml b/vendor/github.com/beevik/ntp/.travis.yml new file mode 100644 index 000000000..ad71df286 --- /dev/null +++ b/vendor/github.com/beevik/ntp/.travis.yml @@ -0,0 +1,15 @@ +language: go +sudo: false + +go: + - 1.7.x + - 1.8.x + - 1.9.x + - tip + +matrix: + allow_failures: + - go: tip + +script: + - go test -v ./... diff --git a/vendor/github.com/casbin/casbin/.gitignore b/vendor/github.com/casbin/casbin/.gitignore new file mode 100644 index 000000000..72ed6f474 --- /dev/null +++ b/vendor/github.com/casbin/casbin/.gitignore @@ -0,0 +1,27 @@ +# 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 +*.prof + +.idea/ +*.iml \ No newline at end of file diff --git a/vendor/github.com/casbin/casbin/.travis.yml b/vendor/github.com/casbin/casbin/.travis.yml new file mode 100644 index 000000000..be7f63415 --- /dev/null +++ b/vendor/github.com/casbin/casbin/.travis.yml @@ -0,0 +1,12 @@ +language: go + +sudo: false + +go: + - tip + +before_install: + - go get github.com/mattn/goveralls + +script: + - $HOME/gopath/bin/goveralls -service=travis-ci \ No newline at end of file diff --git a/vendor/github.com/casbin/casbin/config/config.go b/vendor/github.com/casbin/casbin/config/config.go new file mode 100644 index 000000000..f188dfc85 --- /dev/null +++ b/vendor/github.com/casbin/casbin/config/config.go @@ -0,0 +1,266 @@ +// Copyright 2017 The casbin Authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package config + +import ( + "bufio" + "bytes" + "errors" + "fmt" + "io" + "os" + "strconv" + "strings" + "sync" +) + +var ( + // DEFAULT_SECTION specifies the name of a section if no name provided + DEFAULT_SECTION = "default" + // DEFAULT_COMMENT defines what character(s) indicate a comment `#` + DEFAULT_COMMENT = []byte{'#'} + // DEFAULT_COMMENT_SEM defines what alternate character(s) indicate a comment `;` + DEFAULT_COMMENT_SEM = []byte{';'} + // DEFAULT_MULTI_LINE_SEPARATOR defines what character indicates a multi-line content + DEFAULT_MULTI_LINE_SEPARATOR = []byte{'\\'} +) + +// ConfigInterface defines the behavior of a Config implemenation +type ConfigInterface interface { + String(key string) string + Strings(key string) []string + Bool(key string) (bool, error) + Int(key string) (int, error) + Int64(key string) (int64, error) + Float64(key string) (float64, error) + Set(key string, value string) error +} + +// Config represents an implementation of the ConfigInterface +type Config struct { + // map is not safe. + sync.RWMutex + // Section:key=value + data map[string]map[string]string +} + +// NewConfig create an empty configuration representation from file. +func NewConfig(confName string) (ConfigInterface, error) { + c := &Config{ + data: make(map[string]map[string]string), + } + err := c.parse(confName) + return c, err +} + +// NewConfigFromText create an empty configuration representation from text. +func NewConfigFromText(text string) (ConfigInterface, error) { + c := &Config{ + data: make(map[string]map[string]string), + } + err := c.parseBuffer(bufio.NewReader(strings.NewReader(text))) + return c, err +} + +// AddConfig adds a new section->key:value to the configuration. +func (c *Config) AddConfig(section string, option string, value string) bool { + if section == "" { + section = DEFAULT_SECTION + } + + if _, ok := c.data[section]; !ok { + c.data[section] = make(map[string]string) + } + + _, ok := c.data[section][option] + c.data[section][option] = value + + return !ok +} + +func (c *Config) parse(fname string) (err error) { + c.Lock() + f, err := os.Open(fname) + if err != nil { + return err + } + defer c.Unlock() + defer f.Close() + + buf := bufio.NewReader(f) + return c.parseBuffer(buf) +} + +func (c *Config) parseBuffer(buf *bufio.Reader) error { + var section string + var lineNum int + var buffer bytes.Buffer + var canWrite bool + for { + if canWrite { + if err := c.write(section, lineNum, &buffer); err != nil { + return err + } else { + canWrite = false + } + } + lineNum++ + line, _, err := buf.ReadLine() + if err == io.EOF { + // force write when buffer is not flushed yet + if buffer.Len() > 0 { + if err := c.write(section, lineNum, &buffer); err != nil { + return err + } + } + break + } else if err != nil { + return err + } + + line = bytes.TrimSpace(line) + switch { + case bytes.Equal(line, []byte{}), bytes.HasPrefix(line, DEFAULT_COMMENT_SEM), + bytes.HasPrefix(line, DEFAULT_COMMENT): + canWrite = true + continue + case bytes.HasPrefix(line, []byte{'['}) && bytes.HasSuffix(line, []byte{']'}): + // force write when buffer is not flushed yet + if buffer.Len() > 0 { + if err := c.write(section, lineNum, &buffer); err != nil { + return err + } + canWrite = false + } + section = string(line[1 : len(line)-1]) + default: + var p []byte + if bytes.HasSuffix(line, DEFAULT_MULTI_LINE_SEPARATOR) { + p = bytes.TrimSpace(line[:len(line)-1]) + } else { + p = line + canWrite = true + } + + if _, err := buffer.Write(p); err != nil { + return err + } + } + } + + return nil +} + +func (c *Config) write(section string, lineNum int, b *bytes.Buffer) error { + if b.Len() <= 0 { + return nil + } + + optionVal := bytes.SplitN(b.Bytes(), []byte{'='}, 2) + if len(optionVal) != 2 { + return fmt.Errorf("parse the content error : line %d , %s = ? ", lineNum, optionVal[0]) + } + option := bytes.TrimSpace(optionVal[0]) + value := bytes.TrimSpace(optionVal[1]) + c.AddConfig(section, string(option), string(value)) + + // flush buffer after adding + b.Reset() + + return nil +} + +// Bool lookups up the value using the provided key and converts the value to a bool +func (c *Config) Bool(key string) (bool, error) { + return strconv.ParseBool(c.get(key)) +} + +// Int lookups up the value using the provided key and converts the value to a int +func (c *Config) Int(key string) (int, error) { + return strconv.Atoi(c.get(key)) +} + +// Int64 lookups up the value using the provided key and converts the value to a int64 +func (c *Config) Int64(key string) (int64, error) { + return strconv.ParseInt(c.get(key), 10, 64) +} + +// Float64 lookups up the value using the provided key and converts the value to a float64 +func (c *Config) Float64(key string) (float64, error) { + return strconv.ParseFloat(c.get(key), 64) +} + +// String lookups up the value using the provided key and converts the value to a string +func (c *Config) String(key string) string { + return c.get(key) +} + +// Strings lookups up the value using the provided key and converts the value to an array of string +// by splitting the string by comma +func (c *Config) Strings(key string) []string { + v := c.get(key) + if v == "" { + return nil + } + return strings.Split(v, ",") +} + +// Set sets the value for the specific key in the Config +func (c *Config) Set(key string, value string) error { + c.Lock() + defer c.Unlock() + if len(key) == 0 { + return errors.New("key is empty") + } + + var ( + section string + option string + ) + + keys := strings.Split(strings.ToLower(key), "::") + if len(keys) >= 2 { + section = keys[0] + option = keys[1] + } else { + option = keys[0] + } + + c.AddConfig(section, option, value) + return nil +} + +// section.key or key +func (c *Config) get(key string) string { + var ( + section string + option string + ) + + keys := strings.Split(strings.ToLower(key), "::") + if len(keys) >= 2 { + section = keys[0] + option = keys[1] + } else { + section = DEFAULT_SECTION + option = keys[0] + } + + if value, ok := c.data[section][option]; ok { + return value + } + + return "" +} diff --git a/vendor/github.com/coreos/bbolt/.gitignore b/vendor/github.com/coreos/bbolt/.gitignore new file mode 100644 index 000000000..c7bd2b7a5 --- /dev/null +++ b/vendor/github.com/coreos/bbolt/.gitignore @@ -0,0 +1,4 @@ +*.prof +*.test +*.swp +/bin/ diff --git a/vendor/github.com/cyberdelia/go-metrics-graphite/.gitignore b/vendor/github.com/cyberdelia/go-metrics-graphite/.gitignore new file mode 100644 index 000000000..2c257d4b2 --- /dev/null +++ b/vendor/github.com/cyberdelia/go-metrics-graphite/.gitignore @@ -0,0 +1 @@ +.tags diff --git a/vendor/github.com/cyberdelia/go-metrics-graphite/.travis.yml b/vendor/github.com/cyberdelia/go-metrics-graphite/.travis.yml new file mode 100644 index 000000000..a1afd7cf8 --- /dev/null +++ b/vendor/github.com/cyberdelia/go-metrics-graphite/.travis.yml @@ -0,0 +1,3 @@ +language: go +sudo: false +script: go vet ./... && go test -cover ./... diff --git a/vendor/github.com/danwakefield/fnmatch/.gitignore b/vendor/github.com/danwakefield/fnmatch/.gitignore new file mode 100644 index 000000000..daf913b1b --- /dev/null +++ b/vendor/github.com/danwakefield/fnmatch/.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 +*.test +*.prof diff --git a/vendor/github.com/dlclark/regexp2/.DS_Store b/vendor/github.com/dlclark/regexp2/.DS_Store new file mode 100644 index 0000000000000000000000000000000000000000..2575c69a3762bf83fa611c52b8bbacbcd2cbd416 GIT binary patch literal 6148 zcmeHK%SyvQ6g{J*RtqW!BCfJ@CG-zsZ3RJHRQEpX18P&+2z8gw@T2?y|3=TfGst8b z+ntEaEi)&b+;i{E$t05jVA?NtEnpKsgH5ou#;(HTytElxaw9@E8six4<3V(^b literal 0 HcmV?d00001 diff --git a/vendor/github.com/dlclark/regexp2/.gitignore b/vendor/github.com/dlclark/regexp2/.gitignore new file mode 100644 index 000000000..38a7addcf --- /dev/null +++ b/vendor/github.com/dlclark/regexp2/.gitignore @@ -0,0 +1,25 @@ +# 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 +*.prof +*.out \ No newline at end of file diff --git a/vendor/github.com/dlclark/regexp2/.travis.yml b/vendor/github.com/dlclark/regexp2/.travis.yml new file mode 100644 index 000000000..a24aededa --- /dev/null +++ b/vendor/github.com/dlclark/regexp2/.travis.yml @@ -0,0 +1,5 @@ +language: go + +go: + - 1.5 + - tip \ No newline at end of file diff --git a/vendor/github.com/fortytw2/leaktest/.travis.yml b/vendor/github.com/fortytw2/leaktest/.travis.yml new file mode 100644 index 000000000..5a791be17 --- /dev/null +++ b/vendor/github.com/fortytw2/leaktest/.travis.yml @@ -0,0 +1,16 @@ +language: go +go: + - 1.8 + - 1.9 + - "1.10" + - "1.11" + - tip + +script: + - go test -v -race -parallel 5 -coverprofile=coverage.txt -covermode=atomic ./ + - go test github.com/fortytw2/leaktest -run ^TestEmptyLeak$ + +before_install: + - pip install --user codecov +after_success: + - codecov diff --git a/vendor/github.com/gohxs/readline/.travis.yml b/vendor/github.com/gohxs/readline/.travis.yml new file mode 100644 index 000000000..014a722ac --- /dev/null +++ b/vendor/github.com/gohxs/readline/.travis.yml @@ -0,0 +1,9 @@ +language: go +go: + - 1.5 + - 1.7 +script: + - GOOS=windows go install github.com/chzyer/readline/example/... + - GOOS=linux go install github.com/chzyer/readline/example/... + - GOOS=darwin go install github.com/chzyer/readline/example/... + - go test -race -v diff --git a/vendor/github.com/golang/snappy/.gitignore b/vendor/github.com/golang/snappy/.gitignore new file mode 100644 index 000000000..042091d9b --- /dev/null +++ b/vendor/github.com/golang/snappy/.gitignore @@ -0,0 +1,16 @@ +cmd/snappytool/snappytool +testdata/bench + +# These explicitly listed benchmark data files are for an obsolete version of +# snappy_test.go. +testdata/alice29.txt +testdata/asyoulik.txt +testdata/fireworks.jpeg +testdata/geo.protodata +testdata/html +testdata/html_x_4 +testdata/kppkn.gtb +testdata/lcet10.txt +testdata/paper-100k.pdf +testdata/plrabn12.txt +testdata/urls.10K diff --git a/vendor/github.com/gorilla/context/.travis.yml b/vendor/github.com/gorilla/context/.travis.yml new file mode 100644 index 000000000..6f440f1e4 --- /dev/null +++ b/vendor/github.com/gorilla/context/.travis.yml @@ -0,0 +1,19 @@ +language: go +sudo: false + +matrix: + include: + - go: 1.3 + - go: 1.4 + - go: 1.5 + - go: 1.6 + - go: 1.7 + - go: tip + allow_failures: + - go: tip + +script: + - go get -t -v ./... + - diff -u <(echo -n) <(gofmt -d .) + - go vet $(go list ./... | grep -v /vendor/) + - go test -v -race ./... diff --git a/vendor/github.com/gorilla/handlers/.travis.yml b/vendor/github.com/gorilla/handlers/.travis.yml new file mode 100644 index 000000000..1ba74af10 --- /dev/null +++ b/vendor/github.com/gorilla/handlers/.travis.yml @@ -0,0 +1,20 @@ +language: go +sudo: false + +matrix: + include: + - go: 1.4 + - go: 1.5 + - go: 1.6 + - go: 1.7 + - go: 1.8 + - go: tip + allow_failures: + - go: tip + +script: + - go get -t -v ./... + - diff -u <(echo -n) <(gofmt -d .) + - go vet $(go list ./... | grep -v /vendor/) + - go test -v -race ./... + diff --git a/vendor/github.com/gorilla/mux/.travis.yml b/vendor/github.com/gorilla/mux/.travis.yml new file mode 100644 index 000000000..ad0935dbd --- /dev/null +++ b/vendor/github.com/gorilla/mux/.travis.yml @@ -0,0 +1,23 @@ +language: go +sudo: false + +matrix: + include: + - go: 1.5.x + - go: 1.6.x + - go: 1.7.x + - go: 1.8.x + - go: 1.9.x + - go: 1.10.x + - go: tip + allow_failures: + - go: tip + +install: + - # Skip + +script: + - go get -t -v ./... + - diff -u <(echo -n) <(gofmt -d .) + - go tool vet . + - go test -v -race ./... diff --git a/vendor/github.com/jmoiron/jsonq/.gitignore b/vendor/github.com/jmoiron/jsonq/.gitignore new file mode 100644 index 000000000..dbec55fb6 --- /dev/null +++ b/vendor/github.com/jmoiron/jsonq/.gitignore @@ -0,0 +1 @@ +*.sw[op] diff --git a/vendor/github.com/juju/errors/.gitignore b/vendor/github.com/juju/errors/.gitignore new file mode 100644 index 000000000..836562412 --- /dev/null +++ b/vendor/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/knq/snaker/.travis.yml b/vendor/github.com/knq/snaker/.travis.yml new file mode 100644 index 000000000..b00ec9e99 --- /dev/null +++ b/vendor/github.com/knq/snaker/.travis.yml @@ -0,0 +1,10 @@ +language: go +go: + - 1.9.x + - 1.10.x + - tip +before_install: + - go get github.com/mattn/goveralls +script: + - go test -v -coverprofile=coverage.out + - goveralls -service=travis-ci -coverprofile=coverage.out diff --git a/vendor/github.com/lufia/iostat/.gitignore b/vendor/github.com/lufia/iostat/.gitignore new file mode 100644 index 000000000..a1338d685 --- /dev/null +++ b/vendor/github.com/lufia/iostat/.gitignore @@ -0,0 +1,14 @@ +# Binaries for programs and plugins +*.exe +*.dll +*.so +*.dylib + +# Test binary, build with `go test -c` +*.test + +# Output of the go coverage tool, specifically when used with LiteIDE +*.out + +# Project-local glide cache, RE: https://github.com/Masterminds/glide/issues/736 +.glide/ diff --git a/vendor/github.com/mattn/go-isatty/.travis.yml b/vendor/github.com/mattn/go-isatty/.travis.yml new file mode 100644 index 000000000..5597e026d --- /dev/null +++ b/vendor/github.com/mattn/go-isatty/.travis.yml @@ -0,0 +1,13 @@ +language: go +go: + - tip + +os: + - linux + - osx + +before_install: + - go get github.com/mattn/goveralls + - go get golang.org/x/tools/cmd/cover +script: + - $HOME/gopath/bin/goveralls -repotoken 3gHdORO5k5ziZcWMBxnd9LrMZaJs8m9x5 diff --git a/vendor/github.com/mattn/go-runewidth/.travis.yml b/vendor/github.com/mattn/go-runewidth/.travis.yml new file mode 100644 index 000000000..5c9c2a30f --- /dev/null +++ b/vendor/github.com/mattn/go-runewidth/.travis.yml @@ -0,0 +1,8 @@ +language: go +go: + - tip +before_install: + - go get github.com/mattn/goveralls + - go get golang.org/x/tools/cmd/cover +script: + - $HOME/gopath/bin/goveralls -repotoken lAKAWPzcGsD3A8yBX3BGGtRUdJ6CaGERL diff --git a/vendor/github.com/matttproud/golang_protobuf_extensions/pbutil/.gitignore b/vendor/github.com/matttproud/golang_protobuf_extensions/pbutil/.gitignore new file mode 100644 index 000000000..e16fb946b --- /dev/null +++ b/vendor/github.com/matttproud/golang_protobuf_extensions/pbutil/.gitignore @@ -0,0 +1 @@ +cover.dat diff --git a/vendor/github.com/miekg/dns/.gitignore b/vendor/github.com/miekg/dns/.gitignore new file mode 100644 index 000000000..776cd950c --- /dev/null +++ b/vendor/github.com/miekg/dns/.gitignore @@ -0,0 +1,4 @@ +*.6 +tags +test.out +a.out diff --git a/vendor/github.com/miekg/dns/.travis.yml b/vendor/github.com/miekg/dns/.travis.yml new file mode 100644 index 000000000..18259374e --- /dev/null +++ b/vendor/github.com/miekg/dns/.travis.yml @@ -0,0 +1,18 @@ +language: go +sudo: false + +go: + - 1.10.x + - 1.11.x + - tip + +before_install: + # don't use the miekg/dns when testing forks + - mkdir -p $GOPATH/src/github.com/miekg + - ln -s $TRAVIS_BUILD_DIR $GOPATH/src/github.com/miekg/ || true + +script: + - go test -race -v -bench=. -coverprofile=coverage.txt -covermode=atomic ./... + +after_success: + - bash <(curl -s https://codecov.io/bash) diff --git a/vendor/github.com/minio/blake2b-simd/.gitignore b/vendor/github.com/minio/blake2b-simd/.gitignore new file mode 100644 index 000000000..c56069fe2 --- /dev/null +++ b/vendor/github.com/minio/blake2b-simd/.gitignore @@ -0,0 +1 @@ +*.test \ No newline at end of file diff --git a/vendor/github.com/minio/blake2b-simd/.travis.yml b/vendor/github.com/minio/blake2b-simd/.travis.yml new file mode 100644 index 000000000..545066eb2 --- /dev/null +++ b/vendor/github.com/minio/blake2b-simd/.travis.yml @@ -0,0 +1,21 @@ +sudo: required +dist: trusty +language: go + +os: +- linux +- osx + +osx_image: xcode7.2 + +go: +- 1.6 +- 1.5 + +env: +- ARCH=x86_64 +- ARCH=i686 + +script: +- diff -au <(gofmt -d .) <(printf "") +- go test -race -v ./... diff --git a/vendor/github.com/pkg/errors/.gitignore b/vendor/github.com/pkg/errors/.gitignore new file mode 100644 index 000000000..daf913b1b --- /dev/null +++ b/vendor/github.com/pkg/errors/.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 +*.test +*.prof diff --git a/vendor/github.com/pkg/errors/.travis.yml b/vendor/github.com/pkg/errors/.travis.yml new file mode 100644 index 000000000..588ceca18 --- /dev/null +++ b/vendor/github.com/pkg/errors/.travis.yml @@ -0,0 +1,11 @@ +language: go +go_import_path: github.com/pkg/errors +go: + - 1.4.3 + - 1.5.4 + - 1.6.2 + - 1.7.1 + - tip + +script: + - go test -v ./... diff --git a/vendor/github.com/prometheus/client_golang/prometheus/.gitignore b/vendor/github.com/prometheus/client_golang/prometheus/.gitignore new file mode 100644 index 000000000..3460f0346 --- /dev/null +++ b/vendor/github.com/prometheus/client_golang/prometheus/.gitignore @@ -0,0 +1 @@ +command-line-arguments.test diff --git a/vendor/github.com/prometheus/procfs/.gitignore b/vendor/github.com/prometheus/procfs/.gitignore new file mode 100644 index 000000000..25e3659ab --- /dev/null +++ b/vendor/github.com/prometheus/procfs/.gitignore @@ -0,0 +1 @@ +/fixtures/ diff --git a/vendor/github.com/rcrowley/go-metrics/.gitignore b/vendor/github.com/rcrowley/go-metrics/.gitignore new file mode 100644 index 000000000..83c8f8237 --- /dev/null +++ b/vendor/github.com/rcrowley/go-metrics/.gitignore @@ -0,0 +1,9 @@ +*.[68] +*.a +*.out +*.swp +_obj +_testmain.go +cmd/metrics-bench/metrics-bench +cmd/metrics-example/metrics-example +cmd/never-read/never-read diff --git a/vendor/github.com/rcrowley/go-metrics/.travis.yml b/vendor/github.com/rcrowley/go-metrics/.travis.yml new file mode 100644 index 000000000..117763e65 --- /dev/null +++ b/vendor/github.com/rcrowley/go-metrics/.travis.yml @@ -0,0 +1,17 @@ +language: go + +go: + - 1.3 + - 1.4 + - 1.5 + - 1.6 + - 1.7 + - 1.8 + - 1.9 + +script: + - ./validate.sh + +# this should give us faster builds according to +# http://docs.travis-ci.com/user/migrating-from-legacy/ +sudo: false diff --git a/vendor/github.com/satori/go.uuid/.travis.yml b/vendor/github.com/satori/go.uuid/.travis.yml new file mode 100644 index 000000000..acf820153 --- /dev/null +++ b/vendor/github.com/satori/go.uuid/.travis.yml @@ -0,0 +1,21 @@ +language: go +sudo: false +go: + - 1.6.x + - 1.7.x + - 1.8.x + - 1.9.x + - 1.10.x + - 1.11.x + - tip +matrix: + allow_failures: + - go: tip + fast_finish: true +before_install: + - go get github.com/mattn/goveralls + - go get golang.org/x/tools/cmd/cover +script: + - $HOME/gopath/bin/goveralls -service=travis-ci +notifications: + email: false diff --git a/vendor/github.com/ulule/deepcopier/.gitignore b/vendor/github.com/ulule/deepcopier/.gitignore new file mode 100644 index 000000000..daf913b1b --- /dev/null +++ b/vendor/github.com/ulule/deepcopier/.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 +*.test +*.prof diff --git a/vendor/github.com/ulule/deepcopier/.travis.yml b/vendor/github.com/ulule/deepcopier/.travis.yml new file mode 100644 index 000000000..f03a8ef75 --- /dev/null +++ b/vendor/github.com/ulule/deepcopier/.travis.yml @@ -0,0 +1,10 @@ +language: go +before_install: + - go get github.com/stretchr/testify + - go get github.com/oleiade/reflections + - go get github.com/lib/pq + - go get github.com/guregu/null + - go get github.com/satori/go.uuid +go: + - 1.9 +script: make test diff --git a/vendor/github.com/xo/dburl/.gitignore b/vendor/github.com/xo/dburl/.gitignore new file mode 100644 index 000000000..09fd5ba29 --- /dev/null +++ b/vendor/github.com/xo/dburl/.gitignore @@ -0,0 +1,2 @@ +example/example +coverage.out diff --git a/vendor/github.com/xo/dburl/.travis.yml b/vendor/github.com/xo/dburl/.travis.yml new file mode 100644 index 000000000..e96442ef8 --- /dev/null +++ b/vendor/github.com/xo/dburl/.travis.yml @@ -0,0 +1,10 @@ +language: go +go: + - 1.10.x + - 1.11.x + - tip +before_install: + - go get github.com/mattn/goveralls +script: + - go test -v -coverprofile=coverage.out + - goveralls -service=travis-ci -coverprofile=coverage.out diff --git a/vendor/github.com/xo/tblfmt/.gitignore b/vendor/github.com/xo/tblfmt/.gitignore new file mode 100644 index 000000000..9147a44a1 --- /dev/null +++ b/vendor/github.com/xo/tblfmt/.gitignore @@ -0,0 +1,2 @@ +/example/example +/example/example.exe diff --git a/vendor/github.com/xo/tblfmt/.travis.yml b/vendor/github.com/xo/tblfmt/.travis.yml new file mode 100644 index 000000000..8a32b1c81 --- /dev/null +++ b/vendor/github.com/xo/tblfmt/.travis.yml @@ -0,0 +1,7 @@ +language: go +go: + - 1.10.x + - 1.11.x + - tip +script: + - go test -v -coverprofile=coverage.out diff --git a/vendor/github.com/xo/terminfo/.gitignore b/vendor/github.com/xo/terminfo/.gitignore new file mode 100644 index 000000000..368e0c06c --- /dev/null +++ b/vendor/github.com/xo/terminfo/.gitignore @@ -0,0 +1,9 @@ +/.cache/ + +/cmd/infocmp/infocmp +/cmd/infocmp/.out/ + +/infocmp +/.out/ + +*.txt diff --git a/vendor/github.com/xo/terminfo/.travis.yml b/vendor/github.com/xo/terminfo/.travis.yml new file mode 100644 index 000000000..9389edcb2 --- /dev/null +++ b/vendor/github.com/xo/terminfo/.travis.yml @@ -0,0 +1,15 @@ +language: go +go: + - 1.10.x + - tip +addons: + apt: + sources: + - ubuntu-toolchain-r-test + packages: + - ncurses-bin +before_install: + - go get github.com/mattn/goveralls +script: + - go test -v -coverprofile=coverage.out + - goveralls -service=travis-ci -coverprofile=coverage.out diff --git a/vendor/github.com/xtaci/smux/.gitignore b/vendor/github.com/xtaci/smux/.gitignore new file mode 100644 index 000000000..daf913b1b --- /dev/null +++ b/vendor/github.com/xtaci/smux/.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 +*.test +*.prof diff --git a/vendor/github.com/xtaci/smux/.travis.yml b/vendor/github.com/xtaci/smux/.travis.yml new file mode 100644 index 000000000..5ced9cca7 --- /dev/null +++ b/vendor/github.com/xtaci/smux/.travis.yml @@ -0,0 +1,15 @@ +language: go +go: + - 1.9 + +before_install: + - go get -t -v ./... + +install: + - go get github.com/xtaci/smux + +script: + - go test -coverprofile=coverage.txt -covermode=atomic -bench . + +after_success: + - bash <(curl -s https://codecov.io/bash) diff --git a/vendor/gopkg.in/yaml.v2/.travis.yml b/vendor/gopkg.in/yaml.v2/.travis.yml new file mode 100644 index 000000000..9f556934d --- /dev/null +++ b/vendor/gopkg.in/yaml.v2/.travis.yml @@ -0,0 +1,12 @@ +language: go + +go: + - 1.4 + - 1.5 + - 1.6 + - 1.7 + - 1.8 + - 1.9 + - tip + +go_import_path: gopkg.in/yaml.v2 From 6fbd22fd7b3d7a0e5a7da9ff33458c65aa1ebd92 Mon Sep 17 00:00:00 2001 From: Qi Xiao Date: Tue, 20 Nov 2018 16:55:53 +0800 Subject: [PATCH 08/24] Fix golang sqlite3 driver explain query feature bug --- cmd/cql-mysql-adapter/cursor.go | 16 +++++++++++++++- cmd/cql-secure-gateway/cursor.go | 16 +++++++++++++++- 2 files changed, 30 insertions(+), 2 deletions(-) diff --git a/cmd/cql-mysql-adapter/cursor.go b/cmd/cql-mysql-adapter/cursor.go index 618631fe7..73fe152d5 100644 --- a/cmd/cql-mysql-adapter/cursor.go +++ b/cmd/cql-mysql-adapter/cursor.go @@ -24,6 +24,7 @@ import ( "regexp" "strings" "sync" + "sync/atomic" "github.com/CovenantSQL/CovenantSQL/client" "github.com/CovenantSQL/CovenantSQL/utils/log" @@ -506,8 +507,21 @@ func (c *Cursor) HandleStmtPrepare(query string) (params int, columns int, conte // HACK(xq262144), this feature is supported by sqlite only, replace with query plan analysis for mysql engine // normal statement, using explain to calculate param and result column count // using opcode descriptions in https://www.sqlite.org/opcode.html to resolve + + // resolve parameter count + var paramCount uint32 + sqlparser.Walk(func(node sqlparser.SQLNode) (kontinue bool, err error) { + if v, ok := node.(*sqlparser.SQLVal); ok && v.Type == sqlparser.ValArg { + atomic.AddUint32(¶mCount, 1) + } + return true, nil + }, stmt) + + // build dummy args, golang implementation requires argument to execute explain statement + dummyArgs := make([]interface{}, paramCount) + var rows *sql.Rows - if rows, err = conn.Query("EXPLAIN " + query); err != nil { + if rows, err = conn.Query("EXPLAIN "+query, dummyArgs...); err != nil { err = my.NewError(my.ER_UNKNOWN_ERROR, err.Error()) return } diff --git a/cmd/cql-secure-gateway/cursor.go b/cmd/cql-secure-gateway/cursor.go index f3c33f813..772538cef 100644 --- a/cmd/cql-secure-gateway/cursor.go +++ b/cmd/cql-secure-gateway/cursor.go @@ -24,6 +24,7 @@ import ( "regexp" "strings" "sync" + "sync/atomic" "github.com/CovenantSQL/CovenantSQL/client" "github.com/CovenantSQL/CovenantSQL/utils/log" @@ -507,8 +508,21 @@ func (c *Cursor) HandleStmtPrepare(query string) (params int, columns int, conte // HACK(xq262144), this feature is supported by sqlite only, replace with query plan analysis for mysql engine // normal statement, using explain to calculate param and result column count // using opcode descriptions in https://www.sqlite.org/opcode.html to resolve + + // resolve parameter count + var paramCount uint32 + sqlparser.Walk(func(node sqlparser.SQLNode) (kontinue bool, err error) { + if v, ok := node.(*sqlparser.SQLVal); ok && v.Type == sqlparser.ValArg { + atomic.AddUint32(¶mCount, 1) + } + return true, nil + }, stmt) + + // build dummy args, golang implementation requires argument to execute explain statement + dummyArgs := make([]interface{}, paramCount) + var rows *sql.Rows - if rows, err = conn.Query("EXPLAIN " + query); err != nil { + if rows, err = conn.Query("EXPLAIN "+query, dummyArgs...); err != nil { err = my.NewError(my.ER_UNKNOWN_ERROR, err.Error()) return } From f8b8478bcd72e179627d6671ede4f8cafd41acac Mon Sep 17 00:00:00 2001 From: Qi Xiao Date: Tue, 20 Nov 2018 17:09:31 +0800 Subject: [PATCH 09/24] Disable preparing ddl statement --- cmd/cql-mysql-adapter/cursor.go | 3 +++ cmd/cql-secure-gateway/cursor.go | 3 +++ 2 files changed, 6 insertions(+) diff --git a/cmd/cql-mysql-adapter/cursor.go b/cmd/cql-mysql-adapter/cursor.go index 73fe152d5..33735de4d 100644 --- a/cmd/cql-mysql-adapter/cursor.go +++ b/cmd/cql-mysql-adapter/cursor.go @@ -502,6 +502,9 @@ func (c *Cursor) HandleStmtPrepare(query string) (params int, columns int, conte params = 0 columns = columnCountExplain return + case *sqlparser.DDL: + err = my.NewError(my.ER_UNKNOWN_ERROR, "could not prepare ddl statement") + return } // HACK(xq262144), this feature is supported by sqlite only, replace with query plan analysis for mysql engine diff --git a/cmd/cql-secure-gateway/cursor.go b/cmd/cql-secure-gateway/cursor.go index 772538cef..58874c8e2 100644 --- a/cmd/cql-secure-gateway/cursor.go +++ b/cmd/cql-secure-gateway/cursor.go @@ -503,6 +503,9 @@ func (c *Cursor) HandleStmtPrepare(query string) (params int, columns int, conte params = 0 columns = columnCountExplain return + case *sqlparser.DDL: + err = my.NewError(my.ER_UNKNOWN_ERROR, "could not prepare ddl statement") + return } // HACK(xq262144), this feature is supported by sqlite only, replace with query plan analysis for mysql engine From 3ddf560ba70caccb6fc6ad0aea70a674001cbd08 Mon Sep 17 00:00:00 2001 From: Qi Xiao Date: Thu, 22 Nov 2018 16:45:24 +0800 Subject: [PATCH 10/24] Update mysql-adapter to use new resolver implementation --- cmd/cql-mysql-adapter/cursor.go | 228 ++++------ cmd/cql-mysql-adapter/resolver/meta.go | 261 ++++++++++++ cmd/cql-mysql-adapter/resolver/meta_test.go | 68 +++ cmd/cql-mysql-adapter/resolver/query.go | 49 +++ cmd/cql-mysql-adapter/resolver/resolver.go | 397 ++++++++++++++++++ .../resolver/resolver_test.go | 196 +++++++++ cmd/cql-mysql-adapter/resolver/xxx_test.go | 81 ++++ cmd/cql-mysql-adapter/server.go | 6 +- 8 files changed, 1136 insertions(+), 150 deletions(-) create mode 100644 cmd/cql-mysql-adapter/resolver/meta.go create mode 100644 cmd/cql-mysql-adapter/resolver/meta_test.go create mode 100644 cmd/cql-mysql-adapter/resolver/query.go create mode 100644 cmd/cql-mysql-adapter/resolver/resolver.go create mode 100644 cmd/cql-mysql-adapter/resolver/resolver_test.go create mode 100644 cmd/cql-mysql-adapter/resolver/xxx_test.go diff --git a/cmd/cql-mysql-adapter/cursor.go b/cmd/cql-mysql-adapter/cursor.go index 33735de4d..f17c4d918 100644 --- a/cmd/cql-mysql-adapter/cursor.go +++ b/cmd/cql-mysql-adapter/cursor.go @@ -19,28 +19,17 @@ package main import ( "database/sql" "fmt" - "io" "path/filepath" "regexp" "strings" "sync" - "sync/atomic" "github.com/CovenantSQL/CovenantSQL/client" + "github.com/CovenantSQL/CovenantSQL/cmd/cql-mysql-adapter/resolver" "github.com/CovenantSQL/CovenantSQL/utils/log" - "github.com/CovenantSQL/sqlparser" my "github.com/siddontang/go-mysql/mysql" ) -// preset column counts for special statements -const ( - columnCountShowCreateTable = 1 // sql - columnCountShowTableInfo = 6 // cid|name|type|notnull|dflt_value|pk - columnCountShowIndex = 1 // name - columnCountShowTables = 1 // name - columnCountExplain = 8 // addr|opcode|p1|p2|p3|p4|p5|comment -) - var ( dbIDRegex = regexp.MustCompile("^[a-zA-Z0-9_\\.]+$") specialSelectQuery = regexp.MustCompile("^(?i)SELECT\\s+(DATABASE|USER)\\(\\)\\s*;?\\s*$") @@ -49,7 +38,6 @@ var ( showVariablesQuery = regexp.MustCompile("^(?i)\\s*(?:/\\*.*?\\*/)?\\s*SHOW\\s+VARIABLES.*$") showDatabasesQuery = regexp.MustCompile("^(?i)\\s*(?:/\\*.*?\\*/)?\\s*SHOW\\s+DATABASES.*$") useDatabaseQuery = regexp.MustCompile("^(?i)\\s*USE\\s+`?(\\w+)`?\\s*$") - readQuery = regexp.MustCompile("^(?i)\\s*(?:SELECT|SHOW|DESC)") mysqlServerVariables = map[string]interface{}{ "max_allowed_packet": 255 * 255 * 255, "auto_increment_increment": 1, @@ -65,15 +53,16 @@ var ( // Cursor is a mysql connection handler, like a cursor of normal database. type Cursor struct { - server *Server - curDBLock sync.Mutex + curUser string + curDBLock sync.RWMutex curDB string curDBInstance *sql.DB + curResolver *resolver.Resolver } // NewCursor returns a new cursor. -func NewCursor(s *Server) (c *Cursor) { - return &Cursor{server: s} +func NewCursor() (c *Cursor) { + return &Cursor{} } func (c *Cursor) buildResultSet(rows *sql.Rows) (r *my.Result, err error) { @@ -106,7 +95,13 @@ func (c *Cursor) buildResultSet(rows *sql.Rows) (r *my.Result, err error) { return } -func (c *Cursor) ensureDatabase() (conn *sql.DB, err error) { +func (c *Cursor) getCurDB() string { + c.curDBLock.RLock() + defer c.curDBLock.RUnlock() + return c.curDB +} + +func (c *Cursor) ensureDatabase() (conn *sql.DB, rsv *resolver.Resolver, err error) { c.curDBLock.Lock() defer c.curDBLock.Unlock() @@ -116,6 +111,7 @@ func (c *Cursor) ensureDatabase() (conn *sql.DB, err error) { } conn = c.curDBInstance + rsv = c.curResolver return } @@ -210,10 +206,7 @@ func (c *Cursor) handleSpecialQuery(query string) (r *my.Result, processed bool, processed = true } else if showDatabasesQuery.MatchString(query) { // send show databases result // return result including current database - var curDBStr string - c.curDBLock.Lock() - curDBStr = c.curDB - c.curDBLock.Unlock() + curDBStr := c.getCurDB() var resultSet *my.Resultset @@ -251,16 +244,14 @@ func (c *Cursor) handleSpecialQuery(query string) (r *my.Result, processed bool, switch strings.ToUpper(matches[1]) { case "DATABASE": - c.curDBLock.Lock() resultSet, _ = my.BuildSimpleTextResultset( []string{"DATABASE()"}, - [][]interface{}{{c.curDB}}, + [][]interface{}{{c.getCurDB()}}, ) - c.curDBLock.Unlock() case "USER": resultSet, _ = my.BuildSimpleTextResultset( []string{"USER()"}, - [][]interface{}{{c.server.mysqlUser}}, + [][]interface{}{{c.curUser}}, ) } @@ -287,6 +278,19 @@ func (c *Cursor) UseDB(dbName string) (err error) { return my.NewError(my.ER_BAD_DB_ERROR, fmt.Sprintf("invalid database: %v", dbName)) } + if dbName == c.curDB { + // same database + return + } + + // close previous database and resolver + if c.curResolver != nil { + c.curResolver.Close() + } + if c.curDBInstance != nil { + c.curDBInstance.Close() + } + // connect database cfg := client.NewConfig() cfg.DatabaseID = dbName @@ -299,6 +303,8 @@ func (c *Cursor) UseDB(dbName string) (err error) { c.curDB = dbName c.curDBInstance = db + c.curResolver = resolver.NewResolver() + c.curResolver.RegisterDB(dbName, c.curDBInstance) return } @@ -314,16 +320,25 @@ func (c *Cursor) HandleQuery(query string) (r *my.Result, err error) { return } - var conn *sql.DB + var ( + conn *sql.DB + rsv *resolver.Resolver + q *resolver.Query + ) + + if conn, rsv, err = c.ensureDatabase(); err != nil { + return + } - if conn, err = c.ensureDatabase(); err != nil { + if q, err = rsv.ResolveSingleQuery(c.getCurDB(), query); err != nil { + err = my.NewError(my.ER_SYNTAX_ERROR, err.Error()) return } - // as normal query - if readQuery.MatchString(query) { + // normal query + if q.IsRead() { var rows *sql.Rows - if rows, err = conn.Query(query); err != nil { + if rows, err = conn.Query(q.Query); err != nil { err = my.NewError(my.ER_UNKNOWN_ERROR, err.Error()) return } @@ -333,7 +348,7 @@ func (c *Cursor) HandleQuery(query string) (r *my.Result, err error) { } var result sql.Result - if result, err = conn.Exec(query); err != nil { + if result, err = conn.Exec(q.Query); err != nil { err = my.NewError(my.ER_UNKNOWN_ERROR, err.Error()) return } @@ -355,7 +370,7 @@ func (c *Cursor) HandleQuery(query string) (r *my.Result, err error) { func (c *Cursor) HandleFieldList(table string, fieldWildcard string) (fields []*my.Field, err error) { var conn *sql.DB - if conn, err = c.ensureDatabase(); err != nil { + if conn, _, err = c.ensureDatabase(); err != nil { return } @@ -436,123 +451,27 @@ func (c *Cursor) HandleStmtPrepare(query string) (params int, columns int, conte return } - var conn *sql.DB - - if conn, err = c.ensureDatabase(); err != nil { - return - } - - // tokenize commands var ( - tokenizer = sqlparser.NewStringTokenizer(query) - stmt sqlparser.Statement - lastPos int - singleQuery string + rsv *resolver.Resolver + q *resolver.Query ) - for { - stmt, err = sqlparser.ParseNext(tokenizer) - - // parse statement failed - if err != nil && err != io.EOF { - return - } - - if err == io.EOF { - err = nil - break - } - - if lastPos != 0 { - // multiple queries - err = my.NewError(my.ER_SYNTAX_ERROR, "could not prepare multiple statements") - return - } - - singleQuery = query[lastPos : tokenizer.Position-1] - lastPos = tokenizer.Position + 1 - } - - if strings.TrimSpace(singleQuery) == "" { - // empty query - err = my.NewError(my.ER_SYNTAX_ERROR, "query is empty") + if _, rsv, err = c.ensureDatabase(); err != nil { return } - // calculate param count for special statements - switch s := stmt.(type) { - case *sqlparser.Show: - params = 0 - switch s.Type { - case "table": - if s.ShowCreate { - columns = columnCountShowCreateTable - } else { - columns = columnCountShowTableInfo - } - case "index": - columns = columnCountShowIndex - case "tables": - columns = columnCountShowTables - default: - err = my.NewError(my.ER_UNKNOWN_ERROR, "unknown query") - } - return - case *sqlparser.Explain: - params = 0 - columns = columnCountExplain - return - case *sqlparser.DDL: - err = my.NewError(my.ER_UNKNOWN_ERROR, "could not prepare ddl statement") + if q, err = rsv.ResolveSingleQuery(c.getCurDB(), query); err != nil { + err = my.NewError(my.ER_SYNTAX_ERROR, err.Error()) return } - - // HACK(xq262144), this feature is supported by sqlite only, replace with query plan analysis for mysql engine - // normal statement, using explain to calculate param and result column count - // using opcode descriptions in https://www.sqlite.org/opcode.html to resolve - - // resolve parameter count - var paramCount uint32 - sqlparser.Walk(func(node sqlparser.SQLNode) (kontinue bool, err error) { - if v, ok := node.(*sqlparser.SQLVal); ok && v.Type == sqlparser.ValArg { - atomic.AddUint32(¶mCount, 1) - } - return true, nil - }, stmt) - - // build dummy args, golang implementation requires argument to execute explain statement - dummyArgs := make([]interface{}, paramCount) - - var rows *sql.Rows - if rows, err = conn.Query("EXPLAIN "+query, dummyArgs...); err != nil { - err = my.NewError(my.ER_UNKNOWN_ERROR, err.Error()) + if q.IsDDL() { + err = my.NewError(my.ER_SYNTAX_ERROR, "could not prepare ddl") return } - defer rows.Close() - - var ( - opcode string - p1, p2 int - addr, p3, p4, p5, comment interface{} // not care about this fields - ) - - for rows.Next() { - if err = rows.Scan(&addr, &opcode, &p1, &p2, &p3, &p4, &p5, &comment); err != nil { - err = my.NewError(my.ER_UNKNOWN_ERROR, err.Error()) - return - } - - // p1 for variables is - if opcode == "Variable" && p1 > params { - params = p1 - } - - // p2 is the result column count - if opcode == "ResultRow" { - columns = p2 - } - } + params = q.ParamCount + columns = len(q.ResultColumns) + context = q return } @@ -560,24 +479,30 @@ func (c *Cursor) HandleStmtPrepare(query string) (params int, columns int, conte // HandleStmtExecute handle COM_STMT_EXECUTE, context is the previous one set in prepare // query is the statement prepare query, and args is the params for this statement. func (c *Cursor) HandleStmtExecute(context interface{}, query string, args []interface{}) (r *my.Result, err error) { - // special query - if context != nil { - var ok bool - if r, ok = context.(*my.Result); ok { - return - } + var q *resolver.Query + + switch v := context.(type) { + case *my.Result: + // special query + r = v + return + case *resolver.Query: + q = v + default: + err = my.NewError(my.ER_UNKNOWN_ERROR, "invalid prepared statement") + return } var conn *sql.DB - if conn, err = c.ensureDatabase(); err != nil { + if conn, _, err = c.ensureDatabase(); err != nil { return } // normal query - if readQuery.MatchString(query) { + if q.IsRead() { var rows *sql.Rows - if rows, err = conn.Query(query, args...); err != nil { + if rows, err = conn.Query(q.Query, args...); err != nil { err = my.NewError(my.ER_UNKNOWN_ERROR, err.Error()) return } @@ -587,7 +512,7 @@ func (c *Cursor) HandleStmtExecute(context interface{}, query string, args []int } var result sql.Result - if result, err = conn.Exec(query, args...); err != nil { + if result, err = conn.Exec(q.Query, args...); err != nil { err = my.NewError(my.ER_UNKNOWN_ERROR, err.Error()) return } @@ -616,3 +541,8 @@ func (c *Cursor) HandleStmtClose(context interface{}) (err error) { func (c *Cursor) HandleOtherCommand(cmd byte, data []byte) (err error) { return my.NewError(my.ER_UNKNOWN_ERROR, fmt.Sprintf("command %d is not supported now", cmd)) } + +// SetUser set back current user. +func (c *Cursor) SetUser(user string) { + c.curUser = user +} diff --git a/cmd/cql-mysql-adapter/resolver/meta.go b/cmd/cql-mysql-adapter/resolver/meta.go new file mode 100644 index 000000000..edf7cd9b0 --- /dev/null +++ b/cmd/cql-mysql-adapter/resolver/meta.go @@ -0,0 +1,261 @@ +/* + * Copyright 2018 The CovenantSQL Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package resolver + +import ( + "database/sql" + "fmt" + "github.com/CovenantSQL/CovenantSQL/utils/log" + "github.com/pkg/errors" + "sync" + "time" +) + +const ( + metaRefreshInterval = time.Minute +) + +var ( + ErrDBNotExists = errors.New("database not exists") + ErrTableNotExists = errors.New("table not exists") +) + +type dbHandler interface { + Query(query string, args ...interface{}) (rows *sql.Rows, err error) +} + +type dbMetaHandler struct { + l sync.RWMutex + dbID string + conn dbHandler + tables []string + tableMapping map[string][]string +} + +type metaHandler struct { + l sync.RWMutex + dbMap map[string]*dbMetaHandler + wg sync.WaitGroup + stopCh chan struct{} + stopOnce sync.Once +} + +func NewMetaHandler() (h *metaHandler) { + h = &metaHandler{ + dbMap: make(map[string]*dbMetaHandler), + stopCh: make(chan struct{}), + } + + go h.autoReloadMeta() + + return +} + +func (h *metaHandler) autoReloadMeta() { + for { + select { + case <-h.stopCh: + return + case <-time.After(metaRefreshInterval): + } + h.reloadMeta() + } +} + +func (h *metaHandler) stop() { + h.stopOnce.Do(func() { + if h.stopCh != nil { + select { + case <-h.stopCh: + close(h.stopCh) + default: + } + h.stopCh = nil + } + }) +} + +func (h *metaHandler) addConn(dbID string, conn dbHandler) { + h.l.RLock() + _, exists := h.dbMap[dbID] + h.l.RUnlock() + if !exists { + h.setConn(dbID, conn) + } +} + +func (h *metaHandler) setConn(dbID string, conn dbHandler) { + h.l.Lock() + defer h.l.Unlock() + + h.dbMap[dbID] = NewDBMetaHandler(dbID, conn) +} + +func (h *metaHandler) getConn(dbID string) (conn dbHandler, exists bool) { + h.l.RLock() + defer h.l.RUnlock() + + var dh *dbMetaHandler + + if dh, exists = h.dbMap[dbID]; exists && dh != nil { + conn = dh.conn + } + + return +} + +func (h *metaHandler) getTables(dbID string) (tables []string, err error) { + h.l.RLock() + defer h.l.RUnlock() + + if v := h.dbMap[dbID]; v != nil { + tables, err = v.cacheGetTables() + } else { + err = errors.Wrapf(ErrDBNotExists, "database %v not exists", dbID) + } + + return +} + +func (h *metaHandler) getTable(dbID string, tableName string) (columns []string, err error) { + h.l.RLock() + defer h.l.RUnlock() + + if v := h.dbMap[dbID]; v != nil { + columns, err = v.cacheGetTable(tableName) + } else { + err = errors.Wrapf(ErrDBNotExists, "database %v not exists", dbID) + } + + return +} + +func (h *metaHandler) reloadMeta() { + h.l.RLock() + defer h.l.RUnlock() + + for _, v := range h.dbMap { + v.reloadMeta() + } +} + +func NewDBMetaHandler(dbID string, conn dbHandler) *dbMetaHandler { + return &dbMetaHandler{ + tableMapping: make(map[string][]string), + dbID: dbID, + conn: conn, + } +} + +func (h *dbMetaHandler) reloadMeta() { + h.getTables() + h.l.RLock() + defer h.l.RUnlock() + for t := range h.tableMapping { + delete(h.tableMapping, t) + } +} + +func (h *dbMetaHandler) cacheGetTables() (tables []string, err error) { + h.l.RLock() + tables = append(tables, h.tables...) + h.l.RUnlock() + if len(tables) > 0 { + return + } + + return h.getTables() +} + +func (h *dbMetaHandler) getTables() (tables []string, err error) { + h.l.Lock() + defer h.l.Unlock() + + rows, err := h.conn.Query("SHOW TABLES") + if err != nil { + log.WithError(err).Debug("load tables from database failed") + return + } + + defer rows.Close() + + var ( + tableName string + tempTables []string + ) + + for rows.Next() { + if err = rows.Scan(&tableName); err != nil { + return + } + tempTables = append(tempTables, tableName) + } + + tables = tempTables + h.tables = tables + + return +} + +func (h *dbMetaHandler) cacheGetTable(tableName string) (columns []string, err error) { + h.l.RLock() + columns = append(columns, h.tableMapping[tableName]...) + h.l.RUnlock() + if len(columns) > 0 { + return + } + + // trigger get tables + h.getTables() + + return h.getTable(tableName) +} + +func (h *dbMetaHandler) getTable(tableName string) (columns []string, err error) { + h.l.Lock() + defer h.l.Unlock() + rows, err := h.conn.Query(fmt.Sprintf("DESC `%s`", tableName)) + if err != nil { + log.WithError(err).Debug("load columns from database table failed") + return + } + + defer rows.Close() + + var ( + f1, f3, f4, f5, f6 interface{} + columnName string + tempColumns []string + ) + + for rows.Next() { + if err = rows.Scan(&f1, &columnName, &f3, &f4, &f5, &f6); err != nil { + return + } + tempColumns = append(tempColumns, columnName) + } + + if len(tempColumns) == 0 { + err = errors.Wrapf(ErrTableNotExists, "table %v.%s not exists", h.dbID, tableName) + return + } + + columns = tempColumns + h.tableMapping[tableName] = columns + + return +} diff --git a/cmd/cql-mysql-adapter/resolver/meta_test.go b/cmd/cql-mysql-adapter/resolver/meta_test.go new file mode 100644 index 000000000..75da462fe --- /dev/null +++ b/cmd/cql-mysql-adapter/resolver/meta_test.go @@ -0,0 +1,68 @@ +/* + * Copyright 2018 The CovenantSQL Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package resolver + +import ( + . "github.com/smartystreets/goconvey/convey" + "testing" +) + +func TestMetaHandler(t *testing.T) { + Convey("test handler", t, func() { + mh := NewMetaHandler() + testDBID := randomDBID() + anotherDBID := randomDBID() + db, err := fakeConn([]string{ + "CREATE TABLE test1 (test2 string)", + }) + defer db.Close() + So(err, ShouldBeNil) + mh.addConn(testDBID, db) + result, err := mh.getTables(testDBID) + So(err, ShouldBeNil) + So(result, ShouldResemble, []string{"test1"}) + result, err = mh.getTables(testDBID) + So(err, ShouldBeNil) + So(result, ShouldResemble, []string{"test1"}) + result, err = mh.getTable(testDBID, "test1") + So(err, ShouldBeNil) + So(result, ShouldResemble, []string{"test2"}) + result, err = mh.getTable(testDBID, "test1") + So(err, ShouldBeNil) + So(result, ShouldResemble, []string{"test2"}) + _, err = mh.getTable(testDBID, "test2") + So(err, ShouldNotBeNil) + mh.reloadMeta() + result, err = mh.getTable(testDBID, "test1") + So(err, ShouldBeNil) + So(result, ShouldResemble, []string{"test2"}) + result, err = mh.getTable(testDBID, "test1") + So(err, ShouldBeNil) + So(result, ShouldResemble, []string{"test2"}) + _, err = mh.getTable(testDBID, "test2") + So(err, ShouldNotBeNil) + c, exists := mh.getConn(testDBID) + So(exists, ShouldBeTrue) + So(c, ShouldNotBeNil) + _, exists = mh.getConn(anotherDBID) + So(exists, ShouldBeFalse) + _, err = mh.getTables(anotherDBID) + So(err, ShouldNotBeNil) + _, err = mh.getTable(anotherDBID, "test1") + So(err, ShouldNotBeNil) + }) +} diff --git a/cmd/cql-mysql-adapter/resolver/query.go b/cmd/cql-mysql-adapter/resolver/query.go new file mode 100644 index 000000000..979b3da9e --- /dev/null +++ b/cmd/cql-mysql-adapter/resolver/query.go @@ -0,0 +1,49 @@ +/* + * Copyright 2018 The CovenantSQL Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package resolver + +import "github.com/CovenantSQL/sqlparser" + +type Query struct { + Stmt sqlparser.Statement + Query string + ParamCount int + ResultColumns []string + RelyColumns map[string][]string +} + +func (q *Query) IsDDL() bool { + if q.Stmt != nil { + _, ok := q.Stmt.(*sqlparser.DDL) + return ok + } + + return false +} + +func (q *Query) IsRead() bool { + if q.Stmt != nil { + switch q.Stmt.(type) { + case *sqlparser.Show: + return true + case sqlparser.SelectStatement: + return true + } + } + + return false +} diff --git a/cmd/cql-mysql-adapter/resolver/resolver.go b/cmd/cql-mysql-adapter/resolver/resolver.go new file mode 100644 index 000000000..092b4efe1 --- /dev/null +++ b/cmd/cql-mysql-adapter/resolver/resolver.go @@ -0,0 +1,397 @@ +/* + * Copyright 2018 The CovenantSQL Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package resolver + +import ( + "fmt" + "github.com/CovenantSQL/sqlparser" + "github.com/pkg/errors" + "io" + "sync/atomic" +) + +var ( + ErrQueryLogicError = errors.New("contains logic error in query") + ErrTooMuchQueries = errors.New("too much queries") + + columnsShowCreateTable = []string{"sql"} + columnsShowTableInfo = []string{"cid", "name", "type", "notnull", "dflt_value", "pk"} + columnsShowIndex = []string{"name"} + columnsShowTables = []string{"name"} + columnsExplain = []string{"addr", "opcode", "p1", "p2", "p3", "p4", "p5", "comment"} +) + +type tableColumnMapping struct { + tableDict map[string]int + tableColumns []*tableColumns +} + +type tableColumns struct { + table string + columns []string +} + +func newTableColumnMapping() *tableColumnMapping { + return &tableColumnMapping{ + tableDict: make(map[string]int), + } +} + +func (m *tableColumnMapping) add(table string, columns []string) { + if o, ok := m.tableDict[table]; ok { + if len(m.tableColumns) > o && m.tableColumns[o].table == table { + m.tableColumns[o].columns = append(m.tableColumns[o].columns[:0], columns...) + } + } else { + m.tableColumns = append(m.tableColumns, &tableColumns{ + table: table, + columns: columns, + }) + m.tableDict[table] = len(m.tableColumns) - 1 + } +} + +func (m *tableColumnMapping) appendColumn(table string, columns []string) { + if o, ok := m.tableDict[table]; ok { + if len(m.tableColumns) > o && m.tableColumns[o].table == table { + m.tableColumns[o].columns = append(m.tableColumns[o].columns, columns...) + } + } else { + m.tableColumns = append(m.tableColumns, &tableColumns{ + table: table, + columns: columns, + }) + m.tableDict[table] = len(m.tableColumns) - 1 + } +} + +func (m *tableColumnMapping) clear() { + m.tableDict = make(map[string]int) + m.tableColumns = m.tableColumns[:0] +} + +func (m *tableColumnMapping) union(o *tableColumnMapping) { + if o == nil { + return + } + + for _, v := range o.tableColumns { + if _, exists := m.tableDict[v.table]; !exists { + // add + m.add(v.table, v.columns) + } + } +} + +func (m *tableColumnMapping) update(o *tableColumnMapping) { + if o == nil { + return + } + + for _, v := range o.tableColumns { + m.add(v.table, v.columns) + } +} + +func (m *tableColumnMapping) merge(o *tableColumnMapping) { + if o == nil { + return + } + + for _, v := range o.tableColumns { + m.appendColumn(v.table, v.columns) + } +} + +func (m *tableColumnMapping) get(table string) (columns []string, exists bool) { + var o int + if o, exists = m.tableDict[table]; exists { + if len(m.tableColumns) > o && m.tableColumns[o].table == table { + columns = m.tableColumns[o].columns[:] + return + } + } + + return +} + +type Resolver struct { + meta *metaHandler +} + +func NewResolver() *Resolver { + return &Resolver{ + meta: NewMetaHandler(), + } +} + +func (r *Resolver) Close() { + r.meta.stop() +} + +func (r *Resolver) RegisterDB(dbID string, conn dbHandler) { + r.meta.addConn(dbID, conn) +} + +func (r *Resolver) ResolveQuery(dbID string, query string) (queries []*Query, err error) { + return r.resolveQuery(dbID, query, -1) +} + +func (r *Resolver) ResolveSingleQuery(dbID string, query string) (q *Query, err error) { + var queries []*Query + if queries, err = r.resolveQuery(dbID, query, 1); err == nil && len(queries) > 0 { + q = queries[0] + } + return +} + +func (r *Resolver) Resolve(dbID string, stmt sqlparser.Statement) (q *Query, err error) { + q = &Query{ + Stmt: stmt, + } + if q.ParamCount, err = r.buildParamCount(stmt); err != nil { + return + } + var tableSeq int32 + if q.ResultColumns, err = r.buildResultColumns(&tableSeq, dbID, stmt); err != nil { + return + } + + return +} + +func (r *Resolver) resolveQuery(dbID string, query string, maxQueries int) (queries []*Query, err error) { + var ( + tokenizer = sqlparser.NewStringTokenizer(query) + stmt sqlparser.Statement + q *Query + lastPos int + queryCount int + ) + + for { + stmt, err = sqlparser.ParseNext(tokenizer) + + if err != nil && err != io.EOF { + return + } + + if err == io.EOF { + err = nil + break + } + + queryCount++ + + if maxQueries >= 0 && queryCount > maxQueries { + err = ErrTooMuchQueries + return + } + + q = &Query{ + Stmt: stmt, + Query: query[lastPos : tokenizer.Position-1], + } + lastPos = tokenizer.Position + 1 + if q.ParamCount, err = r.buildParamCount(stmt); err != nil { + return + } + var tableSeq int32 + if q.ResultColumns, err = r.buildResultColumns(&tableSeq, dbID, stmt); err != nil { + return + } + queries = append(queries, q) + } + + return +} + +func (r *Resolver) buildParamCount(stmt sqlparser.Statement) (params int, err error) { + params = 0 + argDedup := make(map[string]bool) + err = sqlparser.Walk(func(node sqlparser.SQLNode) (kontinue bool, err error) { + if v, ok := node.(*sqlparser.SQLVal); ok && v.Type == sqlparser.ValArg { + argVal := string(v.Val) + if !argDedup[argVal] { + argDedup[argVal] = true + params++ + } + } + return true, nil + }, stmt) + return +} + +func (r *Resolver) buildResultColumnsWithTableName(tableSeq *int32, dbID string, stmt sqlparser.SQLNode) ( + columnMapping *tableColumnMapping, err error) { + var ( + tb = sqlparser.NewTrackedBuffer(nil) + tempTableName string + tempColumnsMapping *tableColumnMapping + ) + columnMapping = newTableColumnMapping() + + switch s := stmt.(type) { + case *sqlparser.AliasedTableExpr: + if !s.As.IsEmpty() { + tempTableName = s.As.String() + } else { + tempTableName = fmt.Sprintf("_t%d", atomic.AddInt32(tableSeq, 1)) + } + if tempColumnsMapping, err = r.buildResultColumnsWithTableName(tableSeq, dbID, s.Expr); err != nil { + return + } + for _, v := range tempColumnsMapping.tableColumns { + // merge columns in sub columns + // case: SELECT * FROM (test, test2) AS e; + // will produce columns in test and test2 + columnMapping.appendColumn(tempTableName, v.columns) + } + case *sqlparser.ParenTableExpr: + for _, e := range s.Exprs { + if tempColumnsMapping, err = r.buildResultColumnsWithTableName(tableSeq, dbID, e); err != nil { + return + } + columnMapping.merge(tempColumnsMapping) + } + case *sqlparser.JoinTableExpr: + // no semi join, all fields should be accessible in projection + if tempColumnsMapping, err = r.buildResultColumnsWithTableName(tableSeq, dbID, s.LeftExpr); err != nil { + return + } + columnMapping.merge(tempColumnsMapping) + if tempColumnsMapping, err = r.buildResultColumnsWithTableName(tableSeq, dbID, s.RightExpr); err != nil { + return + } + columnMapping.merge(tempColumnsMapping) + case *sqlparser.Subquery: + // allocate anonymous name + var tempColumns []string + tempTableName = fmt.Sprintf("_t%d", atomic.AddInt32(tableSeq, 1)) + if tempColumns, err = r.buildResultColumns(tableSeq, dbID, s.Select); err != nil { + return + } + columnMapping.add(tempTableName, tempColumns) + case sqlparser.TableName: + tb.Reset() + var tempColumns []string + tempTableName = tb.WriteNode(s).String() + // load table columns from meta + if tempColumns, err = r.meta.getTable(dbID, tempTableName); err != nil { + err = errors.Wrapf(err, "no such table: %v.%s", dbID, tempTableName) + return + } + columnMapping.add(tempTableName, tempColumns) + default: + // invalid query + err = errors.Wrapf(ErrQueryLogicError, "invalid query") + } + return +} + +func (r *Resolver) buildResultColumns(tableSeq *int32, dbID string, stmt sqlparser.Statement) (columns []string, err error) { + switch s := stmt.(type) { + case *sqlparser.Show: + switch s.Type { + case "table": + if s.ShowCreate { + columns = append(columns, columnsShowCreateTable...) + } else { + columns = append(columns, columnsShowTableInfo...) + } + case "index": + columns = append(columns, columnsShowIndex...) + case "tables": + columns = append(columns, columnsShowTables...) + } + case *sqlparser.Explain: + columns = append(columns, columnsExplain...) + case *sqlparser.Union: + // columns should be equalized between left and right fork + var tempColumns []string + if tempColumns, err = r.buildResultColumns(tableSeq, dbID, s.Left); err != nil { + return + } + columns = append(columns, tempColumns...) + case *sqlparser.Select: + // further processing + tb := sqlparser.NewTrackedBuffer(nil) + + // check if contains star expression + var ( + containsStarExpression bool + tableColumns = newTableColumnMapping() + ) + + for _, re := range s.SelectExprs { + if _, ok := re.(*sqlparser.StarExpr); ok { + containsStarExpression = true + } + } + + // load from + if containsStarExpression { + for _, rfe := range s.From { + var tempTableColumns *tableColumnMapping + if tempTableColumns, err = r.buildResultColumnsWithTableName(tableSeq, dbID, rfe); err != nil { + return + } + + tableColumns.merge(tempTableColumns) + } + } + + for _, re := range s.SelectExprs { + switch e := re.(type) { + case *sqlparser.AliasedExpr: + tb.Reset() + var column string + if !e.As.IsEmpty() { + column = e.As.String() + } else { + column = tb.WriteNode(e.Expr).String() + } + columns = append(columns, column) + case *sqlparser.StarExpr: + // * column + // build args from the relied statement or table + if !e.TableName.IsEmpty() { + tb.Reset() + relyTableName := tb.WriteNode(e.TableName).String() + if subColumns, exists := tableColumns.get(relyTableName); exists { + columns = append(columns, subColumns...) + } else { + // not exists, err + err = errors.Wrapf(ErrQueryLogicError, "table %v not found", relyTableName) + return + } + } else { + // combine all columns + for _, v := range tableColumns.tableColumns { + columns = append(columns, v.columns...) + } + } + } + } + case *sqlparser.ParenSelect: + // this branch should not be reached + default: + // query contains no columns + } + + return +} diff --git a/cmd/cql-mysql-adapter/resolver/resolver_test.go b/cmd/cql-mysql-adapter/resolver/resolver_test.go new file mode 100644 index 000000000..e1ff7d876 --- /dev/null +++ b/cmd/cql-mysql-adapter/resolver/resolver_test.go @@ -0,0 +1,196 @@ +/* + * Copyright 2018 The CovenantSQL Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package resolver + +import ( + "github.com/CovenantSQL/sqlparser" + "github.com/pkg/errors" + . "github.com/smartystreets/goconvey/convey" + "testing" +) + +func runTest(c C, r *Resolver, dbID string, query string, paramCount int, resultColumns []string) { + var ( + st sqlparser.Statement + q *Query + err error + ) + st, err = sqlparser.Parse(query) + c.So(err, ShouldBeNil) + q, err = r.Resolve(dbID, st) + c.So(err, ShouldBeNil) + c.So(q.ParamCount, ShouldEqual, paramCount) + c.So(q.ResultColumns, ShouldResemble, resultColumns) +} + +func runErrorTest(c C, r *Resolver, dbID string, query string, expectedErr error) { + var ( + st sqlparser.Statement + err error + ) + st, err = sqlparser.Parse(query) + c.So(err, ShouldBeNil) + _, err = r.Resolve(dbID, st) + c.So(errors.Cause(err), ShouldEqual, expectedErr) +} + +func TestResolver(t *testing.T) { + Convey("resolver", t, func(c C) { + var ( + r *Resolver + testDBID = randomDBID() + db *wrapFakeDB + err error + ) + r = NewResolver() + defer r.Close() + + // inject real tables + db, err = fakeConn([]string{ + "CREATE TABLE test1 (test2 string)", + "CREATE TABLE test3 (test4 string, test5 string, test6 string)", + }) + So(err, ShouldBeNil) + r.RegisterDB(testDBID, db) + defer db.Close() + + // single parameter + runTest(c, r, testDBID, "SELECT ? AS B", 1, []string{"B"}) + // multiple parameter + runTest(c, r, testDBID, "SELECT ? AS A, ? AS B", 2, []string{"A", "B"}) + // single named parameter referenced multiple times + runTest(c, r, testDBID, "SELECT :var AS A, :var AS B", 1, []string{"A", "B"}) + // select from sub query + runTest(c, r, testDBID, "SELECT test FROM (SELECT 1 AS test)", 0, []string{"test"}) + // star select from sub query + runTest(c, r, testDBID, "SELECT * FROM (SELECT 1 AS test)", 0, []string{"test"}) + // named star select from named sub query + runTest(c, r, testDBID, "SELECT t.* FROM (SELECT 1 AS test) AS t", 0, []string{"test"}) + // select from named sub query + runTest(c, r, testDBID, "SELECT test FROM (SELECT 1 AS test) AS t", 0, []string{"test"}) + // select from multiple tables + runTest(c, r, testDBID, "SELECT * FROM (SELECT 1 AS test1), (SELECT 1 AS test2)", 0, []string{"test1", "test2"}) + // select from paren tables + runTest(c, r, testDBID, "SELECT * FROM ((SELECT 1 AS test1), (SELECT 1 AS test2))", 0, []string{"test1", "test2"}) + // named star select from multiple tables + runTest(c, r, testDBID, "SELECT t1.* FROM (SELECT 1 AS test1) AS t1, (SELECT 1 AS test2) AS t2", 0, []string{"test1"}) + // test join + runTest(c, r, testDBID, "SELECT t1.* FROM (SELECT 1 AS test1) AS t1 JOIN (SELECT 1 AS test2) AS t2", 0, []string{"test1"}) + // test union + runTest(c, r, testDBID, "SELECT 1 AS test UNION SELECT 2 AS test", 0, []string{"test"}) + // test special queries + runTest(c, r, testDBID, "SHOW TABLES", 0, columnsShowTables) + runTest(c, r, testDBID, "SHOW INDEX FROM TABLE test", 0, columnsShowIndex) + runTest(c, r, testDBID, "SHOW CREATE TABLE test", 0, columnsShowCreateTable) + runTest(c, r, testDBID, "DESC test", 0, columnsShowTableInfo) + runTest(c, r, testDBID, "EXPLAIN SELECT 1", 0, columnsExplain) + // test no result column query + runTest(c, r, testDBID, "INSERT INTO test VALUES(?)", 1, nil) + // test with real tables + runTest(c, r, testDBID, "SELECT * FROM test1", 0, []string{"test2"}) + // test multiple real tables + runTest(c, r, testDBID, "SELECT * FROM test1, test3", 0, []string{"test2", "test4", "test5", "test6"}) + // projection table not exists in from clause + runErrorTest(c, r, testDBID, "SELECT test3.* FROM test1", ErrQueryLogicError) + // select from non-existence table + runErrorTest(c, r, testDBID, "SELECT * FROM test4", ErrTableNotExists) + + // wrapped single query resolver + var q *Query + q, err = r.ResolveSingleQuery(testDBID, "SELECT 1") + So(err, ShouldBeNil) + So(q, ShouldNotBeNil) + So(q.ParamCount, ShouldEqual, 0) + So(q.ResultColumns, ShouldResemble, []string{"1"}) + + // wrapped multiple query resolver + var queries []*Query + queries, err = r.ResolveQuery(testDBID, "SELECT 1 AS a; SELECT 2 AS b;") + So(err, ShouldBeNil) + So(queries, ShouldHaveLength, 2) + So(queries[0].ParamCount, ShouldEqual, 0) + So(queries[0].ResultColumns, ShouldResemble, []string{"a"}) + So(queries[1].ParamCount, ShouldEqual, 0) + So(queries[1].ResultColumns, ShouldResemble, []string{"b"}) + + // test edge cases + runTest(c, r, testDBID, "SELECT * FROM (SELECT 1 AS test), (SELECT 1 AS test)", 0, []string{"test", "test"}) + }) +} + +func TestColumnMappingUtil(t *testing.T) { + Convey("test column mapping util", t, func() { + cm := newTableColumnMapping() + cm.add("a", []string{"b", "c"}) + cm.add("b", []string{"c", "d"}) + cols, exists := cm.get("a") + So(cols, ShouldResemble, []string{"b", "c"}) + So(exists, ShouldBeTrue) + _, exists = cm.get("c") + So(exists, ShouldBeFalse) + + cm.add("a", []string{"c", "d"}) + cols, exists = cm.get("a") + So(cols, ShouldResemble, []string{"c", "d"}) + So(exists, ShouldBeTrue) + + cm.appendColumn("a", []string{"f", "g"}) + cols, exists = cm.get("a") + So(cols, ShouldResemble, []string{"c", "d", "f", "g"}) + So(exists, ShouldBeTrue) + + cm2 := newTableColumnMapping() + cm2.add("a", []string{"d", "e"}) + cm2.add("f", []string{"d"}) + + cm.union(nil) + _, exists = cm.get("f") + So(exists, ShouldBeFalse) + + cm.union(cm2) + cols, exists = cm.get("a") + So(cols, ShouldResemble, []string{"c", "d", "f", "g"}) + So(exists, ShouldBeTrue) + cols, exists = cm.get("f") + So(cols, ShouldResemble, []string{"d"}) + So(exists, ShouldBeTrue) + + cm.update(nil) + cols, exists = cm.get("a") + So(cols, ShouldResemble, []string{"c", "d", "f", "g"}) + So(exists, ShouldBeTrue) + + cm.update(cm2) + cols, exists = cm.get("a") + So(cols, ShouldResemble, []string{"d", "e"}) + So(exists, ShouldBeTrue) + + cm.merge(nil) + cols, exists = cm.get("a") + So(cols, ShouldResemble, []string{"d", "e"}) + So(exists, ShouldBeTrue) + + cm.merge(cm2) + cols, exists = cm.get("a") + So(cols, ShouldResemble, []string{"d", "e", "d", "e"}) + So(exists, ShouldBeTrue) + + cm.clear() + _, exists = cm.get("a") + So(exists, ShouldBeFalse) + }) +} diff --git a/cmd/cql-mysql-adapter/resolver/xxx_test.go b/cmd/cql-mysql-adapter/resolver/xxx_test.go new file mode 100644 index 000000000..dfa3a5c54 --- /dev/null +++ b/cmd/cql-mysql-adapter/resolver/xxx_test.go @@ -0,0 +1,81 @@ +/* + * Copyright 2018 The CovenantSQL Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package resolver + +import ( + "crypto/rand" + "database/sql" + "github.com/CovenantSQL/CovenantSQL/crypto/hash" + _ "github.com/CovenantSQL/go-sqlite3-encrypt" + "github.com/CovenantSQL/sqlparser" + "github.com/pkg/errors" +) + +type wrapFakeDB struct { + *sql.DB +} + +func (d *wrapFakeDB) Query(query string, args ...interface{}) (rows *sql.Rows, err error) { + var ( + stmt sqlparser.Statement + showStmt *sqlparser.Show + ok bool + ) + if stmt, err = sqlparser.Parse(query); err != nil { + return + } + + if showStmt, ok = stmt.(*sqlparser.Show); !ok { + err = errors.New("invalid query") + return + } + + switch showStmt.Type { + case "tables": + return d.DB.Query("SELECT name FROM sqlite_master WHERE type = \"table\"") + case "table": + return d.DB.Query("PRAGMA table_info(" + showStmt.OnTable.Name.String() + ")") + default: + err = errors.New("invalid query") + return + } +} + +func fakeConn(testCreateTables []string) (db *wrapFakeDB, err error) { + var realDB *sql.DB + if realDB, err = sql.Open("sqlite3", ":memory:"); err != nil { + return + } + + for _, q := range testCreateTables { + if _, err = realDB.Exec(q); err != nil { + return + } + } + + db = &wrapFakeDB{ + DB: realDB, + } + + return +} + +func randomDBID() string { + h := hash.Hash{} + rand.Read(h[:]) + return h.String() +} diff --git a/cmd/cql-mysql-adapter/server.go b/cmd/cql-mysql-adapter/server.go index db58a97d7..455bdeff4 100644 --- a/cmd/cql-mysql-adapter/server.go +++ b/cmd/cql-mysql-adapter/server.go @@ -59,13 +59,17 @@ func (s *Server) Serve() { } func (s *Server) handleConn(conn net.Conn) { - h, err := mys.NewConn(conn, s.mysqlUser, s.mysqlPassword, NewCursor(s)) + cur := NewCursor() + h, err := mys.NewConn(conn, s.mysqlUser, s.mysqlPassword, cur) if err != nil { log.WithError(err).Error("process connection failed") return } + // set connection user to cursor + cur.SetUser(h.GetUser()) + for { err = h.HandleCommand() if err != nil { From 5f7bf25dce64940df2abef1a432a18c9a39bdafe Mon Sep 17 00:00:00 2001 From: Qi Xiao Date: Thu, 22 Nov 2018 17:04:29 +0800 Subject: [PATCH 11/24] Refactor secure gateway code --- cmd/cql-mysql-adapter/{ => cursor}/cursor.go | 2 +- cmd/cql-mysql-adapter/{ => cursor}/scanner.go | 2 +- cmd/cql-mysql-adapter/resolver/meta.go | 5 +- cmd/cql-mysql-adapter/resolver/meta_test.go | 3 +- cmd/cql-mysql-adapter/resolver/resolver.go | 5 +- .../resolver/resolver_test.go | 3 +- cmd/cql-mysql-adapter/resolver/xxx_test.go | 1 + cmd/cql-mysql-adapter/server.go | 3 +- cmd/cql-secure-gateway/cursor.go | 605 +----------------- cmd/cql-secure-gateway/scanner.go | 106 --- cmd/cql-secure-gateway/server.go | 2 +- 11 files changed, 21 insertions(+), 716 deletions(-) rename cmd/cql-mysql-adapter/{ => cursor}/cursor.go (99%) rename cmd/cql-mysql-adapter/{ => cursor}/scanner.go (99%) delete mode 100644 cmd/cql-secure-gateway/scanner.go diff --git a/cmd/cql-mysql-adapter/cursor.go b/cmd/cql-mysql-adapter/cursor/cursor.go similarity index 99% rename from cmd/cql-mysql-adapter/cursor.go rename to cmd/cql-mysql-adapter/cursor/cursor.go index f17c4d918..08698eda0 100644 --- a/cmd/cql-mysql-adapter/cursor.go +++ b/cmd/cql-mysql-adapter/cursor/cursor.go @@ -14,7 +14,7 @@ * limitations under the License. */ -package main +package cursor import ( "database/sql" diff --git a/cmd/cql-mysql-adapter/scanner.go b/cmd/cql-mysql-adapter/cursor/scanner.go similarity index 99% rename from cmd/cql-mysql-adapter/scanner.go rename to cmd/cql-mysql-adapter/cursor/scanner.go index fb96168c2..5fd81dbd3 100644 --- a/cmd/cql-mysql-adapter/scanner.go +++ b/cmd/cql-mysql-adapter/cursor/scanner.go @@ -14,7 +14,7 @@ * limitations under the License. */ -package main +package cursor import ( "database/sql" diff --git a/cmd/cql-mysql-adapter/resolver/meta.go b/cmd/cql-mysql-adapter/resolver/meta.go index edf7cd9b0..9cddd2846 100644 --- a/cmd/cql-mysql-adapter/resolver/meta.go +++ b/cmd/cql-mysql-adapter/resolver/meta.go @@ -19,10 +19,11 @@ package resolver import ( "database/sql" "fmt" - "github.com/CovenantSQL/CovenantSQL/utils/log" - "github.com/pkg/errors" "sync" "time" + + "github.com/CovenantSQL/CovenantSQL/utils/log" + "github.com/pkg/errors" ) const ( diff --git a/cmd/cql-mysql-adapter/resolver/meta_test.go b/cmd/cql-mysql-adapter/resolver/meta_test.go index 75da462fe..06d0853d5 100644 --- a/cmd/cql-mysql-adapter/resolver/meta_test.go +++ b/cmd/cql-mysql-adapter/resolver/meta_test.go @@ -17,8 +17,9 @@ package resolver import ( - . "github.com/smartystreets/goconvey/convey" "testing" + + . "github.com/smartystreets/goconvey/convey" ) func TestMetaHandler(t *testing.T) { diff --git a/cmd/cql-mysql-adapter/resolver/resolver.go b/cmd/cql-mysql-adapter/resolver/resolver.go index 092b4efe1..85be3cea6 100644 --- a/cmd/cql-mysql-adapter/resolver/resolver.go +++ b/cmd/cql-mysql-adapter/resolver/resolver.go @@ -18,10 +18,11 @@ package resolver import ( "fmt" - "github.com/CovenantSQL/sqlparser" - "github.com/pkg/errors" "io" "sync/atomic" + + "github.com/CovenantSQL/sqlparser" + "github.com/pkg/errors" ) var ( diff --git a/cmd/cql-mysql-adapter/resolver/resolver_test.go b/cmd/cql-mysql-adapter/resolver/resolver_test.go index e1ff7d876..db889a976 100644 --- a/cmd/cql-mysql-adapter/resolver/resolver_test.go +++ b/cmd/cql-mysql-adapter/resolver/resolver_test.go @@ -17,10 +17,11 @@ package resolver import ( + "testing" + "github.com/CovenantSQL/sqlparser" "github.com/pkg/errors" . "github.com/smartystreets/goconvey/convey" - "testing" ) func runTest(c C, r *Resolver, dbID string, query string, paramCount int, resultColumns []string) { diff --git a/cmd/cql-mysql-adapter/resolver/xxx_test.go b/cmd/cql-mysql-adapter/resolver/xxx_test.go index dfa3a5c54..47f28468f 100644 --- a/cmd/cql-mysql-adapter/resolver/xxx_test.go +++ b/cmd/cql-mysql-adapter/resolver/xxx_test.go @@ -19,6 +19,7 @@ package resolver import ( "crypto/rand" "database/sql" + "github.com/CovenantSQL/CovenantSQL/crypto/hash" _ "github.com/CovenantSQL/go-sqlite3-encrypt" "github.com/CovenantSQL/sqlparser" diff --git a/cmd/cql-mysql-adapter/server.go b/cmd/cql-mysql-adapter/server.go index 455bdeff4..63372d0b1 100644 --- a/cmd/cql-mysql-adapter/server.go +++ b/cmd/cql-mysql-adapter/server.go @@ -19,6 +19,7 @@ package main import ( "net" + "github.com/CovenantSQL/CovenantSQL/cmd/cql-mysql-adapter/cursor" "github.com/CovenantSQL/CovenantSQL/utils/log" mys "github.com/siddontang/go-mysql/server" ) @@ -59,7 +60,7 @@ func (s *Server) Serve() { } func (s *Server) handleConn(conn net.Conn) { - cur := NewCursor() + cur := cursor.NewCursor() h, err := mys.NewConn(conn, s.mysqlUser, s.mysqlPassword, cur) if err != nil { diff --git a/cmd/cql-secure-gateway/cursor.go b/cmd/cql-secure-gateway/cursor.go index 58874c8e2..5d98cd957 100644 --- a/cmd/cql-secure-gateway/cursor.go +++ b/cmd/cql-secure-gateway/cursor.go @@ -16,609 +16,14 @@ package main -import ( - "database/sql" - "fmt" - "io" - "path/filepath" - "regexp" - "strings" - "sync" - "sync/atomic" +import "github.com/CovenantSQL/CovenantSQL/cmd/cql-mysql-adapter/cursor" - "github.com/CovenantSQL/CovenantSQL/client" - "github.com/CovenantSQL/CovenantSQL/utils/log" - "github.com/CovenantSQL/sqlparser" - my "github.com/siddontang/go-mysql/mysql" -) - -// preset column counts for special statements -const ( - columnCountShowCreateTable = 1 // sql - columnCountShowTableInfo = 6 // cid|name|type|notnull|dflt_value|pk - columnCountShowIndex = 1 // name - columnCountShowTables = 1 // name - columnCountExplain = 8 // addr|opcode|p1|p2|p3|p4|p5|comment -) - -var ( - dbIDRegex = regexp.MustCompile("^[a-zA-Z0-9_\\.]+$") - specialSelectQuery = regexp.MustCompile("^(?i)SELECT\\s+(DATABASE|USER)\\(\\)\\s*;?\\s*$") - emptyResultQuery = regexp.MustCompile("^(?i)\\s*(?:/\\*.*?\\*/)?\\s*(?:SET|ROLLBACK).*$") - emptyResultWithResultSetQuery = regexp.MustCompile("^(?i)\\s*(?:/\\*.*?\\*/)?\\s*(?:(?:SELECT\\s+)?@@(?:\\w+\\.)?|SHOW\\s+WARNINGS).*$") - showVariablesQuery = regexp.MustCompile("^(?i)\\s*(?:/\\*.*?\\*/)?\\s*SHOW\\s+VARIABLES.*$") - showDatabasesQuery = regexp.MustCompile("^(?i)\\s*(?:/\\*.*?\\*/)?\\s*SHOW\\s+DATABASES.*$") - useDatabaseQuery = regexp.MustCompile("^(?i)\\s*USE\\s+`?(\\w+)`?\\s*$") - readQuery = regexp.MustCompile("^(?i)\\s*(?:SELECT|SHOW|DESC)") - mysqlServerVariables = map[string]interface{}{ - "max_allowed_packet": 255 * 255 * 255, - "auto_increment_increment": 1, - "transaction_isolation": "SERIALIZABLE", - "tx_isolation": "SERIALIZABLE", - "transaction_read_only": 0, - "tx_read_only": 0, - "autocommit": 1, - "character_set_server": "utf8", - "collation_server": "utf8_general_ci", - } -) - -// Cursor is a mysql connection handler, like a cursor of normal database. type Cursor struct { - server *Server - curUser string - curDBLock sync.Mutex - curDB string - curDBInstance *sql.DB -} - -// NewCursor returns a new cursor. -func NewCursor(s *Server) (c *Cursor) { - return &Cursor{server: s} -} - -func (c *Cursor) buildResultSet(rows *sql.Rows) (r *my.Result, err error) { - // get columns - var columns []string - if columns, err = rows.Columns(); err != nil { - err = my.NewError(my.ER_UNKNOWN_ERROR, err.Error()) - return - } - - // read all rows - var resultData [][]interface{} - if resultData, err = readAllRows(rows); err != nil { - err = my.NewError(my.ER_UNKNOWN_ERROR, err.Error()) - return - } - - var resultSet *my.Resultset - if resultSet, err = my.BuildSimpleTextResultset(columns, resultData); err != nil { - err = my.NewError(my.ER_UNKNOWN_ERROR, err.Error()) - return - } - - r = &my.Result{ - Status: 0, - InsertId: 0, - AffectedRows: 0, - Resultset: resultSet, - } - return + *cursor.Cursor } -func (c *Cursor) ensureDatabase() (conn *sql.DB, err error) { - c.curDBLock.Lock() - defer c.curDBLock.Unlock() - - if c.curDB == "" { - err = my.NewError(my.ER_NO_DB_ERROR, "select database before any query") - return +func NewCursor() *Cursor { + return &Cursor{ + Cursor: cursor.NewCursor(), } - - conn = c.curDBInstance - - return -} - -func (c *Cursor) detectColumnType(typeStr string) (typeByte uint8) { - typeStr = strings.ToUpper(typeStr) - - if strings.Contains(typeStr, "INT") { - return my.MYSQL_TYPE_LONGLONG - } else if strings.Contains(typeStr, "CHAR") || strings.Contains(typeStr, "CLOB") || - strings.Contains(typeStr, "TEXT") { - return my.MYSQL_TYPE_VAR_STRING - } else if strings.Contains(typeStr, "BLOB") || typeStr == "" { - return my.MYSQL_TYPE_LONG_BLOB - } else if strings.Contains(typeStr, "REAL") || strings.Contains(typeStr, "FLOA") || - strings.Contains(typeStr, "DOUB") { - return my.MYSQL_TYPE_DOUBLE - } else if strings.Contains(typeStr, "BOOLEAN") { - return my.MYSQL_TYPE_BIT - } else if strings.Contains(typeStr, "TIMESTAMP") || strings.Contains(typeStr, "DATETIME") { - return my.MYSQL_TYPE_TIMESTAMP - } else if strings.Contains(typeStr, "TIME") { - return my.MYSQL_TYPE_TIME - } else if strings.Contains(typeStr, "DATE") { - return my.MYSQL_TYPE_DATE - } else { - return my.MYSQL_TYPE_LONG_BLOB - } -} - -func (c *Cursor) handleSpecialQuery(query string) (r *my.Result, processed bool, err error) { - if emptyResultQuery.MatchString(query) { // send empty result for variables query/table listing - // return empty result - r = &my.Result{ - Status: 0, - InsertId: 0, - AffectedRows: 0, - Resultset: nil, - } - processed = true - } else if emptyResultWithResultSetQuery.MatchString(query) { // send empty result include non-nil result set - // return empty result with empty result set - var ( - resultSet *my.Resultset - columns []string - row []interface{} - ) - - for k, v := range mysqlServerVariables { - if strings.Contains(query, k) { - columns = append(columns, k) - row = append(row, v) - } - } - - if len(columns) == 0 { - columns = append(columns, "_") - } - - if row != nil { - resultSet, _ = my.BuildSimpleTextResultset(columns, [][]interface{}{row}) - } else { - resultSet, _ = my.BuildSimpleTextResultset(columns, [][]interface{}{}) - } - - if resultSet.RowDatas == nil { - // force non-empty result set - resultSet.RowDatas = make([]my.RowData, 0) - } - - r = &my.Result{ - Status: 0, - InsertId: 0, - AffectedRows: 0, - Resultset: resultSet, - } - processed = true - } else if showVariablesQuery.MatchString(query) { // send show variables result with custom config - var rows [][]interface{} - - for k, v := range mysqlServerVariables { - rows = append(rows, []interface{}{k, v}) - } - - resultSet, _ := my.BuildSimpleTextResultset([]string{"Variable_name", "Value"}, rows) - r = &my.Result{ - Status: 0, - InsertId: 0, - AffectedRows: 0, - Resultset: resultSet, - } - processed = true - } else if showDatabasesQuery.MatchString(query) { // send show databases result - // return result including current database - var curDBStr string - c.curDBLock.Lock() - curDBStr = c.curDB - c.curDBLock.Unlock() - - var resultSet *my.Resultset - - if curDBStr != "" { - resultSet, _ = my.BuildSimpleTextResultset([]string{"Database"}, [][]interface{}{{curDBStr}}) - } else { - resultSet, _ = my.BuildSimpleTextResultset([]string{"Database"}, nil) - } - - r = &my.Result{ - Status: 0, - InsertId: 0, - AffectedRows: 0, - Resultset: resultSet, - } - processed = true - } else if matches := useDatabaseQuery.FindStringSubmatch(query); len(matches) > 1 { // use database query, same logic as COM_INIT_DB - dbID := matches[1] - - processed = true - if err = c.UseDB(dbID); err == nil { - r = &my.Result{ - Status: 0, - InsertId: 0, - AffectedRows: 0, - Resultset: nil, - } - } - } else if matches := specialSelectQuery.FindStringSubmatch(query); len(matches) > 1 { - // special select database - // for libmysql trivial implementations - // https://github.com/mysql/mysql-server/blob/4f1d7cf5fcb11a3f84cff27e37100d7295e7d5ca/client/mysql.cc#L4266 - - var resultSet *my.Resultset - - switch strings.ToUpper(matches[1]) { - case "DATABASE": - c.curDBLock.Lock() - resultSet, _ = my.BuildSimpleTextResultset( - []string{"DATABASE()"}, - [][]interface{}{{c.curDB}}, - ) - c.curDBLock.Unlock() - case "USER": - resultSet, _ = my.BuildSimpleTextResultset( - []string{"USER()"}, - [][]interface{}{{c.curUser}}, - ) - } - - r = &my.Result{ - Status: 0, - InsertId: 0, - AffectedRows: 0, - Resultset: resultSet, - } - processed = true - } - - return -} - -// UseDB handle COM_INIT_DB command, you can check whether the dbName is valid, or other. -func (c *Cursor) UseDB(dbName string) (err error) { - c.curDBLock.Lock() - defer c.curDBLock.Unlock() - - // test if the database name is a valid database id - if !dbIDRegex.MatchString(dbName) { - // invalid database - return my.NewError(my.ER_BAD_DB_ERROR, fmt.Sprintf("invalid database: %v", dbName)) - } - - // connect database - cfg := client.NewConfig() - cfg.DatabaseID = dbName - - var db *sql.DB - - if db, err = sql.Open("covenantsql", cfg.FormatDSN()); err != nil { - return - } - - c.curDB = dbName - c.curDBInstance = db - - return -} - -// HandleQuery handle COM_QUERY comamnd, like SELECT, INSERT, UPDATE, etc... -// if Result has a Resultset (SELECT, SHOW, etc...), we will send this as the response, otherwise, we will send Result. -func (c *Cursor) HandleQuery(query string) (r *my.Result, err error) { - var processed bool - - log.WithField("query", query).Info("received query") - - if r, processed, err = c.handleSpecialQuery(query); processed { - return - } - - var conn *sql.DB - - if conn, err = c.ensureDatabase(); err != nil { - return - } - - // as normal query - if readQuery.MatchString(query) { - var rows *sql.Rows - if rows, err = conn.Query(query); err != nil { - err = my.NewError(my.ER_UNKNOWN_ERROR, err.Error()) - return - } - - // build result set - return c.buildResultSet(rows) - } - - var result sql.Result - if result, err = conn.Exec(query); err != nil { - err = my.NewError(my.ER_UNKNOWN_ERROR, err.Error()) - return - } - - lastInsertID, _ := result.LastInsertId() - affectedRows, _ := result.RowsAffected() - - r = &my.Result{ - Status: 0, - InsertId: uint64(lastInsertID), - AffectedRows: uint64(affectedRows), - Resultset: nil, - } - - return -} - -// HandleFieldList handle COM_FILED_LIST command. -func (c *Cursor) HandleFieldList(table string, fieldWildcard string) (fields []*my.Field, err error) { - var conn *sql.DB - - if conn, err = c.ensureDatabase(); err != nil { - return - } - - // send show tables command - var columns *sql.Rows - if columns, err = conn.Query(fmt.Sprintf("DESC `%s`", table)); err != nil { - // wrap error - err = my.NewError(my.ER_UNKNOWN_ERROR, err.Error()) - return - } - - defer columns.Close() - - // transform the sql wildcard to glob pattern - var fieldGlob string - - if fieldWildcard != "" { - fieldGlob = strings.NewReplacer("_", "?", "%", "*").Replace(fieldWildcard) - } - - var ( - cid, defaultValue interface{} - columnName, typeString string - isNotNull, isPK bool - ) - - for columns.Next() { - if err = columns.Scan(&cid, &columnName, &typeString, &isNotNull, &defaultValue, &isPK); err != nil { - err = my.NewError(my.ER_UNKNOWN_ERROR, err.Error()) - return - } - - if fieldGlob != "" { - if matched, _ := filepath.Match(fieldGlob, columnName); !matched { - continue - } - } - - // process flag - colFlag := uint16(0) - - if isNotNull { - colFlag |= my.NOT_NULL_FLAG - } - if isPK { - colFlag |= my.NOT_NULL_FLAG - colFlag |= my.PRI_KEY_FLAG - } - - fields = append(fields, &my.Field{ - Name: []byte(columnName), - OrgName: []byte(columnName), - Table: []byte(table), - OrgTable: []byte(table), - Schema: []byte(c.curDB), - Flag: colFlag, - Charset: uint16(my.DEFAULT_COLLATION_ID), - ColumnLength: 0, // no column length specified - Type: c.detectColumnType(typeString), - }) - } - - return -} - -// HandleStmtPrepare handle COM_STMT_PREPARE, params is the param number for this statement, columns is the column number -// context will be used later for statement execute. -func (c *Cursor) HandleStmtPrepare(query string) (params int, columns int, context interface{}, err error) { - // for special queries - var processed bool - var r *my.Result - - log.WithField("query", query).Info("received query") - - if r, processed, err = c.handleSpecialQuery(query); processed { - // set context as result - context = r - return - } - - var conn *sql.DB - - if conn, err = c.ensureDatabase(); err != nil { - return - } - - // tokenize commands - var ( - tokenizer = sqlparser.NewStringTokenizer(query) - stmt sqlparser.Statement - lastPos int - singleQuery string - ) - - for { - stmt, err = sqlparser.ParseNext(tokenizer) - - // parse statement failed - if err != nil && err != io.EOF { - return - } - - if err == io.EOF { - err = nil - break - } - - if lastPos != 0 { - // multiple queries - err = my.NewError(my.ER_SYNTAX_ERROR, "could not prepare multiple statements") - return - } - - singleQuery = query[lastPos : tokenizer.Position-1] - lastPos = tokenizer.Position + 1 - } - - if strings.TrimSpace(singleQuery) == "" { - // empty query - err = my.NewError(my.ER_SYNTAX_ERROR, "query is empty") - return - } - - // calculate param count for special statements - switch s := stmt.(type) { - case *sqlparser.Show: - params = 0 - switch s.Type { - case "table": - if s.ShowCreate { - columns = columnCountShowCreateTable - } else { - columns = columnCountShowTableInfo - } - case "index": - columns = columnCountShowIndex - case "tables": - columns = columnCountShowTables - default: - err = my.NewError(my.ER_UNKNOWN_ERROR, "unknown query") - } - return - case *sqlparser.Explain: - params = 0 - columns = columnCountExplain - return - case *sqlparser.DDL: - err = my.NewError(my.ER_UNKNOWN_ERROR, "could not prepare ddl statement") - return - } - - // HACK(xq262144), this feature is supported by sqlite only, replace with query plan analysis for mysql engine - // normal statement, using explain to calculate param and result column count - // using opcode descriptions in https://www.sqlite.org/opcode.html to resolve - - // resolve parameter count - var paramCount uint32 - sqlparser.Walk(func(node sqlparser.SQLNode) (kontinue bool, err error) { - if v, ok := node.(*sqlparser.SQLVal); ok && v.Type == sqlparser.ValArg { - atomic.AddUint32(¶mCount, 1) - } - return true, nil - }, stmt) - - // build dummy args, golang implementation requires argument to execute explain statement - dummyArgs := make([]interface{}, paramCount) - - var rows *sql.Rows - if rows, err = conn.Query("EXPLAIN "+query, dummyArgs...); err != nil { - err = my.NewError(my.ER_UNKNOWN_ERROR, err.Error()) - return - } - - defer rows.Close() - - var ( - opcode string - p1, p2 int - addr, p3, p4, p5, comment interface{} // not care about this fields - ) - - for rows.Next() { - if err = rows.Scan(&addr, &opcode, &p1, &p2, &p3, &p4, &p5, &comment); err != nil { - err = my.NewError(my.ER_UNKNOWN_ERROR, err.Error()) - return - } - - // p1 for variables is - if opcode == "Variable" && p1 > params { - params = p1 - } - - // p2 is the result column count - if opcode == "ResultRow" { - columns = p2 - } - } - - return -} - -// HandleStmtExecute handle COM_STMT_EXECUTE, context is the previous one set in prepare -// query is the statement prepare query, and args is the params for this statement. -func (c *Cursor) HandleStmtExecute(context interface{}, query string, args []interface{}) (r *my.Result, err error) { - // special query - if context != nil { - var ok bool - if r, ok = context.(*my.Result); ok { - return - } - } - - var conn *sql.DB - - if conn, err = c.ensureDatabase(); err != nil { - return - } - - // normal query - if readQuery.MatchString(query) { - var rows *sql.Rows - if rows, err = conn.Query(query, args...); err != nil { - err = my.NewError(my.ER_UNKNOWN_ERROR, err.Error()) - return - } - - // build result set - return c.buildResultSet(rows) - } - - var result sql.Result - if result, err = conn.Exec(query, args...); err != nil { - err = my.NewError(my.ER_UNKNOWN_ERROR, err.Error()) - return - } - - lastInsertID, _ := result.LastInsertId() - affectedRows, _ := result.RowsAffected() - - r = &my.Result{ - Status: 0, - InsertId: uint64(lastInsertID), - AffectedRows: uint64(affectedRows), - Resultset: nil, - } - - return -} - -// HandleStmtClose handle COM_STMT_CLOSE, context is the previous one set in prepare -// this handler has no response. -func (c *Cursor) HandleStmtClose(context interface{}) (err error) { - return -} - -// HandleOtherCommand handle any other command that is not currently handled by the library, -// default implementation for this method will return an ER_UNKNOWN_ERROR. -func (c *Cursor) HandleOtherCommand(cmd byte, data []byte) (err error) { - return my.NewError(my.ER_UNKNOWN_ERROR, fmt.Sprintf("command %d is not supported now", cmd)) -} - -// SetUser set back current user. -func (c *Cursor) SetUser(user string) { - c.curUser = user } diff --git a/cmd/cql-secure-gateway/scanner.go b/cmd/cql-secure-gateway/scanner.go deleted file mode 100644 index fb96168c2..000000000 --- a/cmd/cql-secure-gateway/scanner.go +++ /dev/null @@ -1,106 +0,0 @@ -/* - * Copyright 2018 The CovenantSQL Authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package main - -import ( - "database/sql" - "io" - "time" -) - -type rowScanner struct { - fieldCnt int - column int // current column - fields []interface{} // temp fields - scanArgs []interface{} -} - -func newRowScanner(fieldCnt int) (s *rowScanner) { - s = &rowScanner{ - fieldCnt: fieldCnt, - column: 0, - fields: make([]interface{}, fieldCnt), - scanArgs: make([]interface{}, fieldCnt), - } - - for i := 0; i != fieldCnt; i++ { - s.scanArgs[i] = s - } - - return -} - -func (s *rowScanner) Scan(src interface{}) error { - if s.fieldCnt <= s.column { - // read complete - return io.EOF - } - - // type conversions - switch srcValue := src.(type) { - case []byte: - s.fields[s.column] = string(srcValue) - case bool: - if srcValue { - s.fields[s.column] = int8(1) - } else { - s.fields[s.column] = int8(0) - } - case time.Time: - s.fields[s.column] = srcValue.String() - default: - s.fields[s.column] = src - } - - s.column++ - - return nil -} - -func (s *rowScanner) GetRow() []interface{} { - return s.fields -} - -func (s *rowScanner) ScanArgs() []interface{} { - // reset - s.column = 0 - s.fields = make([]interface{}, s.fieldCnt) - return s.scanArgs -} - -func readAllRows(rows *sql.Rows) (result [][]interface{}, err error) { - var columns []string - if columns, err = rows.Columns(); err != nil { - return - } - - rs := newRowScanner(len(columns)) - result = make([][]interface{}, 0) - - for rows.Next() { - err = rows.Scan(rs.ScanArgs()...) - if err != nil { - return - } - - result = append(result, rs.GetRow()) - } - - err = rows.Err() - - return -} diff --git a/cmd/cql-secure-gateway/server.go b/cmd/cql-secure-gateway/server.go index b64ec1f65..76364f6e0 100644 --- a/cmd/cql-secure-gateway/server.go +++ b/cmd/cql-secure-gateway/server.go @@ -55,7 +55,7 @@ func (s *Server) Serve() { } func (s *Server) handleConn(conn net.Conn) { - cur := NewCursor(s) + cur := NewCursor() h, err := mys.NewConnWithUsers(conn, s.cfg.AuthConfig.Users, cur) if err != nil { From facefb09cf2c93e2b45dd2d00a2898029a605e95 Mon Sep 17 00:00:00 2001 From: Qi Xiao Date: Thu, 22 Nov 2018 17:08:58 +0800 Subject: [PATCH 12/24] Reload meta after ddl query --- cmd/cql-mysql-adapter/cursor/cursor.go | 4 ++++ cmd/cql-mysql-adapter/resolver/resolver.go | 4 ++++ 2 files changed, 8 insertions(+) diff --git a/cmd/cql-mysql-adapter/cursor/cursor.go b/cmd/cql-mysql-adapter/cursor/cursor.go index 08698eda0..618ee2e0e 100644 --- a/cmd/cql-mysql-adapter/cursor/cursor.go +++ b/cmd/cql-mysql-adapter/cursor/cursor.go @@ -363,6 +363,10 @@ func (c *Cursor) HandleQuery(query string) (r *my.Result, err error) { Resultset: nil, } + if q.IsDDL() { + rsv.ReloadMeta() + } + return } diff --git a/cmd/cql-mysql-adapter/resolver/resolver.go b/cmd/cql-mysql-adapter/resolver/resolver.go index 85be3cea6..cc4472b37 100644 --- a/cmd/cql-mysql-adapter/resolver/resolver.go +++ b/cmd/cql-mysql-adapter/resolver/resolver.go @@ -140,6 +140,10 @@ func NewResolver() *Resolver { } } +func (r *Resolver) ReloadMeta() { + r.meta.reloadMeta() +} + func (r *Resolver) Close() { r.meta.stop() } From 5eaa54db892846654ad0ba0d439293250a16a8f3 Mon Sep 17 00:00:00 2001 From: Qi Xiao Date: Thu, 22 Nov 2018 23:58:44 +0800 Subject: [PATCH 13/24] Update secure gateway resolver --- cmd/cql-mysql-adapter/resolver/meta.go | 76 +- cmd/cql-mysql-adapter/resolver/meta_test.go | 28 +- cmd/cql-mysql-adapter/resolver/query.go | 1 - cmd/cql-mysql-adapter/resolver/resolver.go | 178 +-- .../resolver/resolver_test.go | 54 +- cmd/cql-secure-gateway/resolver.go | 1085 +++++++++++++++++ cmd/cql-secure-gateway/resolver_test.go | 251 ++++ cmd/cql-secure-gateway/xxx_test.go | 81 ++ 8 files changed, 1602 insertions(+), 152 deletions(-) create mode 100644 cmd/cql-secure-gateway/resolver.go create mode 100644 cmd/cql-secure-gateway/resolver_test.go create mode 100644 cmd/cql-secure-gateway/xxx_test.go diff --git a/cmd/cql-mysql-adapter/resolver/meta.go b/cmd/cql-mysql-adapter/resolver/meta.go index 9cddd2846..48d5d2474 100644 --- a/cmd/cql-mysql-adapter/resolver/meta.go +++ b/cmd/cql-mysql-adapter/resolver/meta.go @@ -19,6 +19,7 @@ package resolver import ( "database/sql" "fmt" + "strings" "sync" "time" @@ -27,37 +28,37 @@ import ( ) const ( - metaRefreshInterval = time.Minute + MetaRefreshInterval = time.Minute ) var ( ErrDBNotExists = errors.New("database not exists") - ErrTableNotExists = errors.New("table not exists") + ErrTableNotExists = errors.New("Table not exists") ) -type dbHandler interface { +type DBHandler interface { Query(query string, args ...interface{}) (rows *sql.Rows, err error) } -type dbMetaHandler struct { +type DBMetaHandler struct { l sync.RWMutex dbID string - conn dbHandler + conn DBHandler tables []string tableMapping map[string][]string } -type metaHandler struct { +type MetaHandler struct { l sync.RWMutex - dbMap map[string]*dbMetaHandler + dbMap map[string]*DBMetaHandler wg sync.WaitGroup stopCh chan struct{} stopOnce sync.Once } -func NewMetaHandler() (h *metaHandler) { - h = &metaHandler{ - dbMap: make(map[string]*dbMetaHandler), +func NewMetaHandler() (h *MetaHandler) { + h = &MetaHandler{ + dbMap: make(map[string]*DBMetaHandler), stopCh: make(chan struct{}), } @@ -66,18 +67,18 @@ func NewMetaHandler() (h *metaHandler) { return } -func (h *metaHandler) autoReloadMeta() { +func (h *MetaHandler) autoReloadMeta() { for { select { case <-h.stopCh: return - case <-time.After(metaRefreshInterval): + case <-time.After(MetaRefreshInterval): } - h.reloadMeta() + h.ReloadMeta() } } -func (h *metaHandler) stop() { +func (h *MetaHandler) Stop() { h.stopOnce.Do(func() { if h.stopCh != nil { select { @@ -90,27 +91,27 @@ func (h *metaHandler) stop() { }) } -func (h *metaHandler) addConn(dbID string, conn dbHandler) { +func (h *MetaHandler) AddConn(dbID string, conn DBHandler) { h.l.RLock() _, exists := h.dbMap[dbID] h.l.RUnlock() if !exists { - h.setConn(dbID, conn) + h.SetConn(dbID, conn) } } -func (h *metaHandler) setConn(dbID string, conn dbHandler) { +func (h *MetaHandler) SetConn(dbID string, conn DBHandler) { h.l.Lock() defer h.l.Unlock() h.dbMap[dbID] = NewDBMetaHandler(dbID, conn) } -func (h *metaHandler) getConn(dbID string) (conn dbHandler, exists bool) { +func (h *MetaHandler) GetConn(dbID string) (conn DBHandler, exists bool) { h.l.RLock() defer h.l.RUnlock() - var dh *dbMetaHandler + var dh *DBMetaHandler if dh, exists = h.dbMap[dbID]; exists && dh != nil { conn = dh.conn @@ -119,12 +120,12 @@ func (h *metaHandler) getConn(dbID string) (conn dbHandler, exists bool) { return } -func (h *metaHandler) getTables(dbID string) (tables []string, err error) { +func (h *MetaHandler) GetTables(dbID string) (tables []string, err error) { h.l.RLock() defer h.l.RUnlock() if v := h.dbMap[dbID]; v != nil { - tables, err = v.cacheGetTables() + tables, err = v.CacheGetTables() } else { err = errors.Wrapf(ErrDBNotExists, "database %v not exists", dbID) } @@ -132,12 +133,12 @@ func (h *metaHandler) getTables(dbID string) (tables []string, err error) { return } -func (h *metaHandler) getTable(dbID string, tableName string) (columns []string, err error) { +func (h *MetaHandler) GetTable(dbID string, tableName string) (columns []string, err error) { h.l.RLock() defer h.l.RUnlock() if v := h.dbMap[dbID]; v != nil { - columns, err = v.cacheGetTable(tableName) + columns, err = v.CacheGetTable(tableName) } else { err = errors.Wrapf(ErrDBNotExists, "database %v not exists", dbID) } @@ -145,25 +146,25 @@ func (h *metaHandler) getTable(dbID string, tableName string) (columns []string, return } -func (h *metaHandler) reloadMeta() { +func (h *MetaHandler) ReloadMeta() { h.l.RLock() defer h.l.RUnlock() for _, v := range h.dbMap { - v.reloadMeta() + v.ReloadMeta() } } -func NewDBMetaHandler(dbID string, conn dbHandler) *dbMetaHandler { - return &dbMetaHandler{ +func NewDBMetaHandler(dbID string, conn DBHandler) *DBMetaHandler { + return &DBMetaHandler{ tableMapping: make(map[string][]string), dbID: dbID, conn: conn, } } -func (h *dbMetaHandler) reloadMeta() { - h.getTables() +func (h *DBMetaHandler) ReloadMeta() { + h.GetTables() h.l.RLock() defer h.l.RUnlock() for t := range h.tableMapping { @@ -171,7 +172,7 @@ func (h *dbMetaHandler) reloadMeta() { } } -func (h *dbMetaHandler) cacheGetTables() (tables []string, err error) { +func (h *DBMetaHandler) CacheGetTables() (tables []string, err error) { h.l.RLock() tables = append(tables, h.tables...) h.l.RUnlock() @@ -179,10 +180,10 @@ func (h *dbMetaHandler) cacheGetTables() (tables []string, err error) { return } - return h.getTables() + return h.GetTables() } -func (h *dbMetaHandler) getTables() (tables []string, err error) { +func (h *DBMetaHandler) GetTables() (tables []string, err error) { h.l.Lock() defer h.l.Unlock() @@ -212,8 +213,9 @@ func (h *dbMetaHandler) getTables() (tables []string, err error) { return } -func (h *dbMetaHandler) cacheGetTable(tableName string) (columns []string, err error) { +func (h *DBMetaHandler) CacheGetTable(tableName string) (columns []string, err error) { h.l.RLock() + tableName = strings.ToLower(tableName) columns = append(columns, h.tableMapping[tableName]...) h.l.RUnlock() if len(columns) > 0 { @@ -221,12 +223,12 @@ func (h *dbMetaHandler) cacheGetTable(tableName string) (columns []string, err e } // trigger get tables - h.getTables() + h.GetTables() - return h.getTable(tableName) + return h.GetTable(tableName) } -func (h *dbMetaHandler) getTable(tableName string) (columns []string, err error) { +func (h *DBMetaHandler) GetTable(tableName string) (columns []string, err error) { h.l.Lock() defer h.l.Unlock() rows, err := h.conn.Query(fmt.Sprintf("DESC `%s`", tableName)) @@ -256,7 +258,7 @@ func (h *dbMetaHandler) getTable(tableName string) (columns []string, err error) } columns = tempColumns - h.tableMapping[tableName] = columns + h.tableMapping[strings.ToLower(tableName)] = columns return } diff --git a/cmd/cql-mysql-adapter/resolver/meta_test.go b/cmd/cql-mysql-adapter/resolver/meta_test.go index 06d0853d5..993850b9e 100644 --- a/cmd/cql-mysql-adapter/resolver/meta_test.go +++ b/cmd/cql-mysql-adapter/resolver/meta_test.go @@ -32,38 +32,38 @@ func TestMetaHandler(t *testing.T) { }) defer db.Close() So(err, ShouldBeNil) - mh.addConn(testDBID, db) - result, err := mh.getTables(testDBID) + mh.AddConn(testDBID, db) + result, err := mh.GetTables(testDBID) So(err, ShouldBeNil) So(result, ShouldResemble, []string{"test1"}) - result, err = mh.getTables(testDBID) + result, err = mh.GetTables(testDBID) So(err, ShouldBeNil) So(result, ShouldResemble, []string{"test1"}) - result, err = mh.getTable(testDBID, "test1") + result, err = mh.GetTable(testDBID, "test1") So(err, ShouldBeNil) So(result, ShouldResemble, []string{"test2"}) - result, err = mh.getTable(testDBID, "test1") + result, err = mh.GetTable(testDBID, "test1") So(err, ShouldBeNil) So(result, ShouldResemble, []string{"test2"}) - _, err = mh.getTable(testDBID, "test2") + _, err = mh.GetTable(testDBID, "test2") So(err, ShouldNotBeNil) - mh.reloadMeta() - result, err = mh.getTable(testDBID, "test1") + mh.ReloadMeta() + result, err = mh.GetTable(testDBID, "test1") So(err, ShouldBeNil) So(result, ShouldResemble, []string{"test2"}) - result, err = mh.getTable(testDBID, "test1") + result, err = mh.GetTable(testDBID, "test1") So(err, ShouldBeNil) So(result, ShouldResemble, []string{"test2"}) - _, err = mh.getTable(testDBID, "test2") + _, err = mh.GetTable(testDBID, "test2") So(err, ShouldNotBeNil) - c, exists := mh.getConn(testDBID) + c, exists := mh.GetConn(testDBID) So(exists, ShouldBeTrue) So(c, ShouldNotBeNil) - _, exists = mh.getConn(anotherDBID) + _, exists = mh.GetConn(anotherDBID) So(exists, ShouldBeFalse) - _, err = mh.getTables(anotherDBID) + _, err = mh.GetTables(anotherDBID) So(err, ShouldNotBeNil) - _, err = mh.getTable(anotherDBID, "test1") + _, err = mh.GetTable(anotherDBID, "test1") So(err, ShouldNotBeNil) }) } diff --git a/cmd/cql-mysql-adapter/resolver/query.go b/cmd/cql-mysql-adapter/resolver/query.go index 979b3da9e..1e040619f 100644 --- a/cmd/cql-mysql-adapter/resolver/query.go +++ b/cmd/cql-mysql-adapter/resolver/query.go @@ -23,7 +23,6 @@ type Query struct { Query string ParamCount int ResultColumns []string - RelyColumns map[string][]string } func (q *Query) IsDDL() bool { diff --git a/cmd/cql-mysql-adapter/resolver/resolver.go b/cmd/cql-mysql-adapter/resolver/resolver.go index cc4472b37..5619456f4 100644 --- a/cmd/cql-mysql-adapter/resolver/resolver.go +++ b/cmd/cql-mysql-adapter/resolver/resolver.go @@ -36,93 +36,125 @@ var ( columnsExplain = []string{"addr", "opcode", "p1", "p2", "p3", "p4", "p5", "comment"} ) -type tableColumnMapping struct { - tableDict map[string]int - tableColumns []*tableColumns +type TableColumnMapping struct { + TableDict map[string]int + TableColumns []*TableColumns } -type tableColumns struct { - table string - columns []string +type TableColumns struct { + Table string + Columns []string } -func newTableColumnMapping() *tableColumnMapping { - return &tableColumnMapping{ - tableDict: make(map[string]int), +func NewTableColumnMapping() *TableColumnMapping { + return &TableColumnMapping{ + TableDict: make(map[string]int), } } -func (m *tableColumnMapping) add(table string, columns []string) { - if o, ok := m.tableDict[table]; ok { - if len(m.tableColumns) > o && m.tableColumns[o].table == table { - m.tableColumns[o].columns = append(m.tableColumns[o].columns[:0], columns...) +func (m *TableColumnMapping) Add(table string, columns []string) { + if o, ok := m.TableDict[table]; ok { + if len(m.TableColumns) > o && m.TableColumns[o].Table == table { + m.TableColumns[o].Columns = append(m.TableColumns[o].Columns[:0], columns...) } } else { - m.tableColumns = append(m.tableColumns, &tableColumns{ - table: table, - columns: columns, + m.TableColumns = append(m.TableColumns, &TableColumns{ + Table: table, + Columns: columns, }) - m.tableDict[table] = len(m.tableColumns) - 1 + m.TableDict[table] = len(m.TableColumns) - 1 } } -func (m *tableColumnMapping) appendColumn(table string, columns []string) { - if o, ok := m.tableDict[table]; ok { - if len(m.tableColumns) > o && m.tableColumns[o].table == table { - m.tableColumns[o].columns = append(m.tableColumns[o].columns, columns...) +func (m *TableColumnMapping) AppendColumn(table string, columns []string) { + if o, ok := m.TableDict[table]; ok { + if len(m.TableColumns) > o && m.TableColumns[o].Table == table { + m.TableColumns[o].Columns = append(m.TableColumns[o].Columns, columns...) } } else { - m.tableColumns = append(m.tableColumns, &tableColumns{ - table: table, - columns: columns, + m.TableColumns = append(m.TableColumns, &TableColumns{ + Table: table, + Columns: columns, }) - m.tableDict[table] = len(m.tableColumns) - 1 + m.TableDict[table] = len(m.TableColumns) - 1 } } -func (m *tableColumnMapping) clear() { - m.tableDict = make(map[string]int) - m.tableColumns = m.tableColumns[:0] +func (m *TableColumnMapping) UnionColumn(table string, columns []string) { + if o, ok := m.TableDict[table]; ok { + if len(m.TableColumns) > o && m.TableColumns[o].Table == table { + columnMap := make(map[string]bool, len(m.TableColumns[o].Columns)) + for _, c := range m.TableColumns[o].Columns { + columnMap[c] = true + } + for _, c := range columns { + if !columnMap[c] { + m.TableColumns[o].Columns = append(m.TableColumns[o].Columns, c) + } + } + } + } else { + m.TableColumns = append(m.TableColumns, &TableColumns{ + Table: table, + Columns: columns, + }) + m.TableDict[table] = len(m.TableColumns) - 1 + } +} + +func (m *TableColumnMapping) Clear() { + m.TableDict = make(map[string]int) + m.TableColumns = m.TableColumns[:0] } -func (m *tableColumnMapping) union(o *tableColumnMapping) { +func (m *TableColumnMapping) Union(o *TableColumnMapping) { if o == nil { return } - for _, v := range o.tableColumns { - if _, exists := m.tableDict[v.table]; !exists { + for _, v := range o.TableColumns { + if _, exists := m.TableDict[v.Table]; !exists { // add - m.add(v.table, v.columns) + m.Add(v.Table, v.Columns) } } } -func (m *tableColumnMapping) update(o *tableColumnMapping) { +func (m *TableColumnMapping) Update(o *TableColumnMapping) { + if o == nil { + return + } + + for _, v := range o.TableColumns { + m.Add(v.Table, v.Columns) + } +} + +func (m *TableColumnMapping) Merge(o *TableColumnMapping) { if o == nil { return } - for _, v := range o.tableColumns { - m.add(v.table, v.columns) + for _, v := range o.TableColumns { + m.AppendColumn(v.Table, v.Columns) } } -func (m *tableColumnMapping) merge(o *tableColumnMapping) { +func (m *TableColumnMapping) MergeDistinct(o *TableColumnMapping) { if o == nil { return } - for _, v := range o.tableColumns { - m.appendColumn(v.table, v.columns) + for _, v := range o.TableColumns { + m.UnionColumn(v.Table, v.Columns) } } -func (m *tableColumnMapping) get(table string) (columns []string, exists bool) { +func (m *TableColumnMapping) Get(table string) (columns []string, exists bool) { var o int - if o, exists = m.tableDict[table]; exists { - if len(m.tableColumns) > o && m.tableColumns[o].table == table { - columns = m.tableColumns[o].columns[:] + if o, exists = m.TableDict[table]; exists { + if len(m.TableColumns) > o && m.TableColumns[o].Table == table { + columns = m.TableColumns[o].Columns[:] return } } @@ -131,25 +163,25 @@ func (m *tableColumnMapping) get(table string) (columns []string, exists bool) { } type Resolver struct { - meta *metaHandler + Meta *MetaHandler } func NewResolver() *Resolver { return &Resolver{ - meta: NewMetaHandler(), + Meta: NewMetaHandler(), } } func (r *Resolver) ReloadMeta() { - r.meta.reloadMeta() + r.Meta.ReloadMeta() } func (r *Resolver) Close() { - r.meta.stop() + r.Meta.Stop() } -func (r *Resolver) RegisterDB(dbID string, conn dbHandler) { - r.meta.addConn(dbID, conn) +func (r *Resolver) RegisterDB(dbID string, conn DBHandler) { + r.Meta.AddConn(dbID, conn) } func (r *Resolver) ResolveQuery(dbID string, query string) (queries []*Query, err error) { @@ -168,11 +200,11 @@ func (r *Resolver) Resolve(dbID string, stmt sqlparser.Statement) (q *Query, err q = &Query{ Stmt: stmt, } - if q.ParamCount, err = r.buildParamCount(stmt); err != nil { + if q.ParamCount, err = r.BuildParamCount(stmt); err != nil { return } var tableSeq int32 - if q.ResultColumns, err = r.buildResultColumns(&tableSeq, dbID, stmt); err != nil { + if q.ResultColumns, err = r.BuildResultColumns(&tableSeq, dbID, stmt); err != nil { return } @@ -212,11 +244,11 @@ func (r *Resolver) resolveQuery(dbID string, query string, maxQueries int) (quer Query: query[lastPos : tokenizer.Position-1], } lastPos = tokenizer.Position + 1 - if q.ParamCount, err = r.buildParamCount(stmt); err != nil { + if q.ParamCount, err = r.BuildParamCount(stmt); err != nil { return } var tableSeq int32 - if q.ResultColumns, err = r.buildResultColumns(&tableSeq, dbID, stmt); err != nil { + if q.ResultColumns, err = r.BuildResultColumns(&tableSeq, dbID, stmt); err != nil { return } queries = append(queries, q) @@ -225,7 +257,7 @@ func (r *Resolver) resolveQuery(dbID string, query string, maxQueries int) (quer return } -func (r *Resolver) buildParamCount(stmt sqlparser.Statement) (params int, err error) { +func (r *Resolver) BuildParamCount(stmt sqlparser.Statement) (params int, err error) { params = 0 argDedup := make(map[string]bool) err = sqlparser.Walk(func(node sqlparser.SQLNode) (kontinue bool, err error) { @@ -242,13 +274,13 @@ func (r *Resolver) buildParamCount(stmt sqlparser.Statement) (params int, err er } func (r *Resolver) buildResultColumnsWithTableName(tableSeq *int32, dbID string, stmt sqlparser.SQLNode) ( - columnMapping *tableColumnMapping, err error) { + columnMapping *TableColumnMapping, err error) { var ( tb = sqlparser.NewTrackedBuffer(nil) tempTableName string - tempColumnsMapping *tableColumnMapping + tempColumnsMapping *TableColumnMapping ) - columnMapping = newTableColumnMapping() + columnMapping = NewTableColumnMapping() switch s := stmt.(type) { case *sqlparser.AliasedTableExpr: @@ -260,47 +292,47 @@ func (r *Resolver) buildResultColumnsWithTableName(tableSeq *int32, dbID string, if tempColumnsMapping, err = r.buildResultColumnsWithTableName(tableSeq, dbID, s.Expr); err != nil { return } - for _, v := range tempColumnsMapping.tableColumns { + for _, v := range tempColumnsMapping.TableColumns { // merge columns in sub columns // case: SELECT * FROM (test, test2) AS e; // will produce columns in test and test2 - columnMapping.appendColumn(tempTableName, v.columns) + columnMapping.AppendColumn(tempTableName, v.Columns) } case *sqlparser.ParenTableExpr: for _, e := range s.Exprs { if tempColumnsMapping, err = r.buildResultColumnsWithTableName(tableSeq, dbID, e); err != nil { return } - columnMapping.merge(tempColumnsMapping) + columnMapping.Merge(tempColumnsMapping) } case *sqlparser.JoinTableExpr: // no semi join, all fields should be accessible in projection if tempColumnsMapping, err = r.buildResultColumnsWithTableName(tableSeq, dbID, s.LeftExpr); err != nil { return } - columnMapping.merge(tempColumnsMapping) + columnMapping.Merge(tempColumnsMapping) if tempColumnsMapping, err = r.buildResultColumnsWithTableName(tableSeq, dbID, s.RightExpr); err != nil { return } - columnMapping.merge(tempColumnsMapping) + columnMapping.Merge(tempColumnsMapping) case *sqlparser.Subquery: // allocate anonymous name var tempColumns []string tempTableName = fmt.Sprintf("_t%d", atomic.AddInt32(tableSeq, 1)) - if tempColumns, err = r.buildResultColumns(tableSeq, dbID, s.Select); err != nil { + if tempColumns, err = r.BuildResultColumns(tableSeq, dbID, s.Select); err != nil { return } - columnMapping.add(tempTableName, tempColumns) + columnMapping.Add(tempTableName, tempColumns) case sqlparser.TableName: tb.Reset() var tempColumns []string tempTableName = tb.WriteNode(s).String() // load table columns from meta - if tempColumns, err = r.meta.getTable(dbID, tempTableName); err != nil { + if tempColumns, err = r.Meta.GetTable(dbID, tempTableName); err != nil { err = errors.Wrapf(err, "no such table: %v.%s", dbID, tempTableName) return } - columnMapping.add(tempTableName, tempColumns) + columnMapping.Add(tempTableName, tempColumns) default: // invalid query err = errors.Wrapf(ErrQueryLogicError, "invalid query") @@ -308,7 +340,7 @@ func (r *Resolver) buildResultColumnsWithTableName(tableSeq *int32, dbID string, return } -func (r *Resolver) buildResultColumns(tableSeq *int32, dbID string, stmt sqlparser.Statement) (columns []string, err error) { +func (r *Resolver) BuildResultColumns(tableSeq *int32, dbID string, stmt sqlparser.Statement) (columns []string, err error) { switch s := stmt.(type) { case *sqlparser.Show: switch s.Type { @@ -328,7 +360,7 @@ func (r *Resolver) buildResultColumns(tableSeq *int32, dbID string, stmt sqlpars case *sqlparser.Union: // columns should be equalized between left and right fork var tempColumns []string - if tempColumns, err = r.buildResultColumns(tableSeq, dbID, s.Left); err != nil { + if tempColumns, err = r.BuildResultColumns(tableSeq, dbID, s.Left); err != nil { return } columns = append(columns, tempColumns...) @@ -339,7 +371,7 @@ func (r *Resolver) buildResultColumns(tableSeq *int32, dbID string, stmt sqlpars // check if contains star expression var ( containsStarExpression bool - tableColumns = newTableColumnMapping() + tableColumns = NewTableColumnMapping() ) for _, re := range s.SelectExprs { @@ -351,12 +383,12 @@ func (r *Resolver) buildResultColumns(tableSeq *int32, dbID string, stmt sqlpars // load from if containsStarExpression { for _, rfe := range s.From { - var tempTableColumns *tableColumnMapping + var tempTableColumns *TableColumnMapping if tempTableColumns, err = r.buildResultColumnsWithTableName(tableSeq, dbID, rfe); err != nil { return } - tableColumns.merge(tempTableColumns) + tableColumns.Merge(tempTableColumns) } } @@ -377,7 +409,7 @@ func (r *Resolver) buildResultColumns(tableSeq *int32, dbID string, stmt sqlpars if !e.TableName.IsEmpty() { tb.Reset() relyTableName := tb.WriteNode(e.TableName).String() - if subColumns, exists := tableColumns.get(relyTableName); exists { + if subColumns, exists := tableColumns.Get(relyTableName); exists { columns = append(columns, subColumns...) } else { // not exists, err @@ -386,8 +418,8 @@ func (r *Resolver) buildResultColumns(tableSeq *int32, dbID string, stmt sqlpars } } else { // combine all columns - for _, v := range tableColumns.tableColumns { - columns = append(columns, v.columns...) + for _, v := range tableColumns.TableColumns { + columns = append(columns, v.Columns...) } } } diff --git a/cmd/cql-mysql-adapter/resolver/resolver_test.go b/cmd/cql-mysql-adapter/resolver/resolver_test.go index db889a976..aee7e3b78 100644 --- a/cmd/cql-mysql-adapter/resolver/resolver_test.go +++ b/cmd/cql-mysql-adapter/resolver/resolver_test.go @@ -135,63 +135,63 @@ func TestResolver(t *testing.T) { func TestColumnMappingUtil(t *testing.T) { Convey("test column mapping util", t, func() { - cm := newTableColumnMapping() - cm.add("a", []string{"b", "c"}) - cm.add("b", []string{"c", "d"}) - cols, exists := cm.get("a") + cm := NewTableColumnMapping() + cm.Add("a", []string{"b", "c"}) + cm.Add("b", []string{"c", "d"}) + cols, exists := cm.Get("a") So(cols, ShouldResemble, []string{"b", "c"}) So(exists, ShouldBeTrue) - _, exists = cm.get("c") + _, exists = cm.Get("c") So(exists, ShouldBeFalse) - cm.add("a", []string{"c", "d"}) - cols, exists = cm.get("a") + cm.Add("a", []string{"c", "d"}) + cols, exists = cm.Get("a") So(cols, ShouldResemble, []string{"c", "d"}) So(exists, ShouldBeTrue) - cm.appendColumn("a", []string{"f", "g"}) - cols, exists = cm.get("a") + cm.AppendColumn("a", []string{"f", "g"}) + cols, exists = cm.Get("a") So(cols, ShouldResemble, []string{"c", "d", "f", "g"}) So(exists, ShouldBeTrue) - cm2 := newTableColumnMapping() - cm2.add("a", []string{"d", "e"}) - cm2.add("f", []string{"d"}) + cm2 := NewTableColumnMapping() + cm2.Add("a", []string{"d", "e"}) + cm2.Add("f", []string{"d"}) - cm.union(nil) - _, exists = cm.get("f") + cm.Union(nil) + _, exists = cm.Get("f") So(exists, ShouldBeFalse) - cm.union(cm2) - cols, exists = cm.get("a") + cm.Union(cm2) + cols, exists = cm.Get("a") So(cols, ShouldResemble, []string{"c", "d", "f", "g"}) So(exists, ShouldBeTrue) - cols, exists = cm.get("f") + cols, exists = cm.Get("f") So(cols, ShouldResemble, []string{"d"}) So(exists, ShouldBeTrue) - cm.update(nil) - cols, exists = cm.get("a") + cm.Update(nil) + cols, exists = cm.Get("a") So(cols, ShouldResemble, []string{"c", "d", "f", "g"}) So(exists, ShouldBeTrue) - cm.update(cm2) - cols, exists = cm.get("a") + cm.Update(cm2) + cols, exists = cm.Get("a") So(cols, ShouldResemble, []string{"d", "e"}) So(exists, ShouldBeTrue) - cm.merge(nil) - cols, exists = cm.get("a") + cm.Merge(nil) + cols, exists = cm.Get("a") So(cols, ShouldResemble, []string{"d", "e"}) So(exists, ShouldBeTrue) - cm.merge(cm2) - cols, exists = cm.get("a") + cm.Merge(cm2) + cols, exists = cm.Get("a") So(cols, ShouldResemble, []string{"d", "e", "d", "e"}) So(exists, ShouldBeTrue) - cm.clear() - _, exists = cm.get("a") + cm.Clear() + _, exists = cm.Get("a") So(exists, ShouldBeFalse) }) } diff --git a/cmd/cql-secure-gateway/resolver.go b/cmd/cql-secure-gateway/resolver.go new file mode 100644 index 000000000..c53b33c59 --- /dev/null +++ b/cmd/cql-secure-gateway/resolver.go @@ -0,0 +1,1085 @@ +/* + * Copyright 2018 The CovenantSQL Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package main + +import ( + "strings" + + "github.com/CovenantSQL/CovenantSQL/cmd/cql-mysql-adapter/resolver" + "github.com/CovenantSQL/sqlparser" + "github.com/pkg/errors" +) + +var ( + ErrInvalidStatement = errors.New("invalid statement") + ErrInvalidColumn = errors.New("invalid column") + ErrInvalidTable = errors.New("invalid table") + ErrAmbiguousColumnName = errors.New("ambiguous column name") +) + +type Resolver struct { + *resolver.Resolver +} + +type Query struct { + *resolver.Query +} + +type Columns []*Column + +type Computation struct { + Op string + Operands Columns +} + +func (c Columns) ResolveColName(col *sqlparser.ColName) (column *Column, err error) { + if col == nil { + return + } + + if col.Qualifier.IsEmpty() { + return c.ResolveColIdent(col.Name) + } + + for _, item := range c { + if strings.EqualFold(item.ColName.Qualifier.Name.String(), col.Qualifier.Name.String()) && + item.ColName.Name.Equal(col.Name) { + column = item + return + } + } + + // not found + tb := sqlparser.NewTrackedBuffer(nil) + err = errors.Wrapf(ErrInvalidColumn, "column %s not found", tb.WriteNode(col).String()) + return +} + +func (c Columns) ResolveColIdent(col sqlparser.ColIdent) (column *Column, err error) { + found := false + for _, item := range c { + if item.ColName.Name.Equal(col) { + // found + if found { + // ambiguous + tb1 := sqlparser.NewTrackedBuffer(nil) + tb2 := sqlparser.NewTrackedBuffer(nil) + err = errors.Wrapf(ErrAmbiguousColumnName, + "ambiguous column %s, candidates: %s, %s", col.String(), + tb1.WriteNode(&column.ColName).String(), + tb2.WriteNode(&item.ColName).String()) + return + } + + found = true + column = item + } + } + + if !found { + err = errors.Wrapf(ErrInvalidColumn, "column %s not found", col.String()) + return + } + + return +} + +func NewColumns(cols ...*Column) Columns { + return Columns(cols) +} + +type Column struct { + ColName sqlparser.ColName + IsPhysical bool + Computation *Computation +} + +type ColumnResult struct { + TableName string + ColName string + Ops [][]string +} + +func (r *Resolver) buildExpression(dbID string, expr sqlparser.Expr, originColumns Columns) (cols Columns, err error) { + if expr == nil { + return + } + + var ( + tempColumn *Column + tempColumns Columns + ) + + switch e := expr.(type) { + case *sqlparser.ColName: + // find column in original columns + if tempColumn, err = originColumns.ResolveColName(e); err != nil { + return + } + cols = append(cols, tempColumn) + case *sqlparser.AndExpr: + if tempColumns, err = r.buildExpression(dbID, e.Left, originColumns); err != nil { + return + } + cols = append(cols, tempColumns...) + if tempColumns, err = r.buildExpression(dbID, e.Right, originColumns); err != nil { + return + } + cols = append(cols, tempColumns...) + cols = NewColumns(&Column{ + IsPhysical: false, + Computation: &Computation{ + Op: "and", + Operands: cols, + }, + }) + case *sqlparser.OrExpr: + if tempColumns, err = r.buildExpression(dbID, e.Left, originColumns); err != nil { + return + } + cols = append(cols, tempColumns...) + if tempColumns, err = r.buildExpression(dbID, e.Right, originColumns); err != nil { + return + } + cols = append(cols, tempColumns...) + cols = NewColumns(&Column{ + IsPhysical: false, + Computation: &Computation{ + Op: "or", + Operands: cols, + }, + }) + case *sqlparser.NotExpr: + if tempColumns, err = r.buildExpression(dbID, e.Expr, originColumns); err != nil { + return + } + cols = NewColumns(&Column{ + IsPhysical: false, + Computation: &Computation{ + Op: "not", + Operands: tempColumns, + }, + }) + case *sqlparser.ParenExpr: + return r.buildExpression(dbID, e.Expr, originColumns) + + /* literal constant */ + case sqlparser.BoolVal: + cols = NewColumns(&Column{ + IsPhysical: false, + Computation: &Computation{ + Op: "bool_literal", + }, + }) + case *sqlparser.NullVal: + cols = NewColumns(&Column{ + IsPhysical: false, + Computation: &Computation{ + Op: "null_literal", + }, + }) + case *sqlparser.TimeExpr: + // treat time expression as literal + cols = NewColumns(&Column{ + IsPhysical: false, + Computation: &Computation{ + Op: "time_literal", + }, + }) + case sqlparser.ValTuple: + for _, v := range e { + if tempColumns, err = r.buildExpression(dbID, v, originColumns); err != nil { + return + } + cols = append(cols, tempColumns...) + } + case *sqlparser.SQLVal: + switch e.Type { + case sqlparser.StrVal, sqlparser.HexVal: + cols = NewColumns(&Column{ + IsPhysical: false, + Computation: &Computation{ + Op: "string_literal", + }, + }) + case sqlparser.IntVal, sqlparser.FloatVal, sqlparser.HexNum, sqlparser.BitVal: + cols = NewColumns(&Column{ + IsPhysical: false, + Computation: &Computation{ + Op: "numeric_literal", + }, + }) + case sqlparser.ValArg: + cols = NewColumns(&Column{ + IsPhysical: false, + Computation: &Computation{ + Op: "parameter", + }, + }) + default: + tb := sqlparser.NewTrackedBuffer(nil) + err = errors.Wrapf(ErrInvalidField, "invalid field %s", tb.WriteNode(e).String()) + } + + /* expression with operators */ + case *sqlparser.BinaryExpr: + if tempColumns, err = r.buildExpression(dbID, e.Left, originColumns); err != nil { + return + } + cols = append(cols, tempColumns...) + if tempColumns, err = r.buildExpression(dbID, e.Right, originColumns); err != nil { + return + } + cols = append(cols, tempColumns...) + cols = NewColumns(&Column{ + IsPhysical: false, + Computation: &Computation{ + Op: "binary_op", + Operands: cols, + }, + }) + case *sqlparser.CaseExpr: + conditionExprs := []sqlparser.Expr{e.Expr} + resultExprs := []sqlparser.Expr{e.Else} + for _, w := range e.Whens { + conditionExprs = append(conditionExprs, w.Cond) + resultExprs = append(resultExprs, w.Val) + } + + for _, expr := range conditionExprs { + if tempColumns, err = r.buildExpression(dbID, expr, originColumns); err != nil { + return + } + cols = append(cols, tempColumns...) + cols = append(cols, &Column{ + IsPhysical: false, + Computation: &Computation{ + Op: "case_condition", + Operands: tempColumns, + }, + }) + } + + for _, expr := range resultExprs { + if tempColumns, err = r.buildExpression(dbID, expr, originColumns); err != nil { + return + } + cols = append(cols, tempColumns...) + } + cols = NewColumns(&Column{ + IsPhysical: false, + Computation: &Computation{ + Op: "case", + Operands: cols, + }, + }) + case *sqlparser.ComparisonExpr: + for _, expr := range []sqlparser.Expr{e.Left, e.Right, e.Escape} { + if tempColumns, err = r.buildExpression(dbID, expr, originColumns); err != nil { + return + } + cols = append(cols, tempColumns...) + } + + cols = NewColumns(&Column{ + IsPhysical: false, + Computation: &Computation{ + Op: "compare", + Operands: cols, + }, + }) + case *sqlparser.ConvertExpr: + if tempColumns, err = r.buildExpression(dbID, e.Expr, originColumns); err != nil { + return + } + cols = NewColumns(&Column{ + IsPhysical: false, + Computation: &Computation{ + Op: "convert", + Operands: tempColumns, + }, + }) + case *sqlparser.ExistsExpr: + // inject current symbols to select expression parents + if e.Subquery != nil { + if tempColumns, err = r.buildSelectStatement(dbID, e.Subquery.Select, originColumns); err != nil { + return + } + cols = NewColumns(&Column{ + IsPhysical: false, + Computation: &Computation{ + Op: "exists", + Operands: tempColumns, + }, + }) + } + case *sqlparser.FuncExpr: + if tempColumns, err = r.buildSelectExprs(dbID, e.Exprs, e.Distinct, originColumns); err != nil { + return + } + cols = NewColumns(&Column{ + IsPhysical: false, + Computation: &Computation{ + Op: "function", + Operands: tempColumns, + }, + }) + case *sqlparser.GroupConcatExpr: + if tempColumns, err = r.buildSelectExprs(dbID, e.Exprs, e.Distinct != "", originColumns); err != nil { + return + } + cols = append(cols, tempColumns...) + if tempColumns, err = r.buildOrderBy(dbID, e.OrderBy, cols); err != nil { + return + } + cols = append(cols, tempColumns...) + cols = NewColumns(&Column{ + IsPhysical: false, + Computation: &Computation{ + Op: "group_concat", + Operands: cols, + }, + }) + case *sqlparser.IntervalExpr: + if tempColumns, err = r.buildExpression(dbID, e.Expr, originColumns); err != nil { + return + } + cols = NewColumns(&Column{ + IsPhysical: false, + Computation: &Computation{ + Op: "interval", + Operands: tempColumns, + }, + }) + case *sqlparser.IsExpr: + if tempColumns, err = r.buildExpression(dbID, e.Expr, originColumns); err != nil { + return + } + cols = NewColumns(&Column{ + IsPhysical: false, + Computation: &Computation{ + Op: "is", + Operands: tempColumns, + }, + }) + case *sqlparser.SubstrExpr: + if tempColumns, err = r.buildExpression(dbID, e.From, originColumns); err != nil { + return + } + cols = append(cols, tempColumns...) + if tempColumns, err = r.buildExpression(dbID, e.To, originColumns); err != nil { + return + } + cols = append(cols, tempColumns...) + if tempColumns, err = r.buildExpression(dbID, e.Name, originColumns); err != nil { + return + } + cols = append(cols, tempColumns...) + cols = NewColumns(&Column{ + IsPhysical: false, + Computation: &Computation{ + Op: "substr", + Operands: cols, + }, + }) + case *sqlparser.RangeCond: + for _, expr := range []sqlparser.Expr{e.Left, e.From, e.To} { + if tempColumns, err = r.buildExpression(dbID, expr, originColumns); err != nil { + return + } + cols = append(cols, tempColumns...) + } + cols = NewColumns(&Column{ + IsPhysical: false, + Computation: &Computation{ + Op: "range", + Operands: cols, + }, + }) + case *sqlparser.UnaryExpr: + if tempColumns, err = r.buildExpression(dbID, e.Expr, originColumns); err != nil { + return + } + cols = NewColumns(&Column{ + IsPhysical: false, + Computation: &Computation{ + Op: "unary_op", + Operands: cols, + }, + }) + case *sqlparser.Subquery: + // should contains single result + if tempColumns, err = r.buildSelectStatement(dbID, e.Select, originColumns); err != nil { + return + } + + if len(tempColumns) != 1 { + err = errors.Wrapf(ErrInvalidStatement, "sub query returns %d columns", len(tempColumns)) + return + } + + cols = NewColumns(&Column{ + IsPhysical: false, + Computation: &Computation{ + Op: "sub_query", + Operands: tempColumns, + }, + }) + default: + tb := sqlparser.NewTrackedBuffer(nil) + err = errors.Wrapf(ErrInvalidField, "invalid expression %s", tb.WriteNode(expr).String()) + return + } + + return +} + +func (r *Resolver) buildOrder(dbID string, order *sqlparser.Order, originColumns Columns) (cols Columns, err error) { + if order == nil { + return + } + + if cols, err = r.buildExpression(dbID, order.Expr, originColumns); err != nil { + return + } + + if len(cols) != 1 { + err = errors.Wrapf(ErrInvalidStatement, "sub query returns %d columns", len(cols)) + return + } + + // add order by operator on it + cols = NewColumns(&Column{ + IsPhysical: false, + Computation: &Computation{ + Op: "order_by", + Operands: cols, + }, + }) + return +} + +func (r *Resolver) buildGroupBy(dbID string, groupBy sqlparser.GroupBy, originColumns Columns) (cols Columns, err error) { + var tempColumns Columns + for _, ge := range groupBy { + if tempColumns, err = r.buildExpression(dbID, ge, originColumns); err != nil { + return + } + cols = append(cols, tempColumns...) + + // add group by column operator to it + cols = append(cols, &Column{ + IsPhysical: false, + Computation: &Computation{ + Op: "group_by", + Operands: tempColumns, + }, + }) + } + return +} + +func (r *Resolver) buildOrderBy(dbID string, orders sqlparser.OrderBy, originColumns Columns) (cols Columns, err error) { + var tempColumns Columns + for _, oe := range orders { + if tempColumns, err = r.buildOrder(dbID, oe, originColumns); err != nil { + return + } + cols = append(cols, tempColumns...) + } + return +} + +func (r *Resolver) buildWhere(dbID string, whereCond *sqlparser.Where, originColumns Columns) (cols Columns, err error) { + if whereCond == nil { + return + } + + return r.buildExpression(dbID, whereCond.Expr, originColumns) +} + +func (r *Resolver) buildAllColumnInTable(dbID string, tableName sqlparser.TableName) (cols Columns, err error) { + var ( + columns []string + tblName = tableName.Name.String() + ) + + if columns, err = r.Meta.GetTable(dbID, tblName); err != nil { + return + } + + for _, c := range columns { + cols = append(cols, &Column{ + ColName: sqlparser.ColName{ + Qualifier: tableName, + Name: sqlparser.NewColIdent(c), + }, + IsPhysical: true, + }) + } + return +} + +func (r *Resolver) buildAllColumnInTableExpr(dbID string, tableExpr sqlparser.TableExpr, extraColumns Columns) (cols Columns, err error) { + switch te := tableExpr.(type) { + case *sqlparser.AliasedTableExpr: + var tempColumns Columns + + switch se := te.Expr.(type) { + case *sqlparser.Subquery: + if tempColumns, err = r.buildSelectStatement(dbID, se.Select, nil); err != nil { + return + } + case sqlparser.TableName: + if tempColumns, err = r.buildAllColumnInTable(dbID, se); err != nil { + return + } + default: + tb := sqlparser.NewTrackedBuffer(nil) + err = errors.Wrapf(ErrInvalidStatement, "invalid table expression %s", tb.WriteNode(te.Expr).String()) + return + } + + // table alias + if !te.As.IsEmpty() { + for _, c := range tempColumns { + cols = append(cols, &Column{ + ColName: sqlparser.ColName{ + Qualifier: sqlparser.TableName{ + Name: te.As, + }, + Name: c.ColName.Name, + }, + IsPhysical: false, + Computation: &Computation{ + Op: "alias", + Operands: Columns([]*Column{c}), + }, + }) + } + } else { + cols = tempColumns + } + + return + case *sqlparser.JoinTableExpr: + var ( + leftColumns Columns + rightColumns Columns + tempColumns Columns + ) + if leftColumns, err = r.buildAllColumnInTableExpr(dbID, te.LeftExpr, extraColumns); err != nil { + return + } + cols = append(cols, leftColumns...) + if rightColumns, err = r.buildAllColumnInTableExpr(dbID, te.RightExpr, extraColumns); err != nil { + return + } + cols = append(cols, rightColumns...) + // ensure using column exists in both sets + for _, uc := range te.Condition.Using { + // should be in left and right + args := make(Columns, 0, 2) + + for _, cs := range []Columns{leftColumns, rightColumns} { + found := false + + for _, c := range cs { + if uc.Equal(c.ColName.Name) { + found = true + args = append(args, c) + break + } + } + + if !found { + err = errors.Wrapf(ErrInvalidColumn, "column not found %s", uc.String()) + return + } + } + + cols = append(cols, &Column{ + IsPhysical: false, + Computation: &Computation{ + Op: "join_using", + Operands: args, + }, + }) + } + // ensure join on condition usage + if tempColumns, err = r.buildExpression(dbID, te.Condition.On, cols); err != nil { + return + } + cols = append(cols, tempColumns...) + return + case *sqlparser.ParenTableExpr: + var tempColumns Columns + for _, tableExpr := range te.Exprs { + if tempColumns, err = r.buildAllColumnInTableExpr(dbID, tableExpr, extraColumns); err != nil { + return + } + + cols = append(cols, tempColumns...) + } + return + default: + tb := sqlparser.NewTrackedBuffer(nil) + err = errors.Wrapf(ErrInvalidStatement, "invalid table expression %s", tb.WriteNode(tableExpr).String()) + return + } +} + +func (r *Resolver) buildAllColumnInTableExprs(dbID string, tableExprs sqlparser.TableExprs, extraColumns Columns) (cols Columns, err error) { + var tempColumns Columns + for _, tableExpr := range tableExprs { + if tempColumns, err = r.buildAllColumnInTableExpr(dbID, tableExpr, extraColumns); err != nil { + return + } + cols = append(cols, tempColumns...) + } + return +} + +func (r *Resolver) buildLimit(dbID string, limitCond *sqlparser.Limit) (cols Columns, err error) { + if limitCond == nil { + return + } + + var tempColumns Columns + if tempColumns, err = r.buildExpression(dbID, limitCond.Offset, nil); err != nil { + return + } + cols = append(cols, tempColumns...) + if tempColumns, err = r.buildExpression(dbID, limitCond.Rowcount, nil); err != nil { + return + } + cols = append(cols, tempColumns...) + return +} + +func (r *Resolver) buildUpdateExprs(dbID string, updateExprs sqlparser.UpdateExprs, originColumns Columns) (cols Columns, err error) { + for _, ue := range updateExprs { + colTableName := ue.Name.Qualifier.Name.String() + found := false + + for _, oc := range originColumns { + if colTableName != "" && !strings.EqualFold(oc.ColName.Qualifier.Name.String(), colTableName) { + // table name not matched + continue + } + + if ue.Name.Name.Equal(oc.ColName.Name) { + // matched + found = true + cols = append(cols, oc) + } + } + + if !found { + tb := sqlparser.NewTrackedBuffer(nil) + err = errors.Wrapf(ErrInvalidColumn, "no such column %s", tb.WriteNode(ue).String()) + return + } + } + + return +} + +func (r *Resolver) buildInsertColumns(dbID string, insertCols sqlparser.Columns, originColumns Columns) (cols Columns, err error) { + for _, ic := range insertCols { + found := false + + for _, oc := range originColumns { + if oc.ColName.Name.Equal(ic) { + // matched + cols = append(cols, oc) + found = true + break + } + } + + if !found { + err = errors.Wrapf(ErrInvalidColumn, "no such column %s", ic.String()) + return + } + } + + return +} + +func (r *Resolver) buildInsertRows(dbID string, insertRows sqlparser.InsertRows) (cols Columns, err error) { + switch n := insertRows.(type) { + case sqlparser.SelectStatement: + return r.buildSelectStatement(dbID, n, nil) + case sqlparser.Values: + // parse values + var tempCols Columns + for _, v := range n { + if tempCols, err = r.buildExpression(dbID, sqlparser.Expr(v), nil); err != nil { + return + } + cols = append(cols, tempCols...) + } + return + default: + tb := sqlparser.NewTrackedBuffer(nil) + err = errors.Wrapf(ErrInvalidStatement, "invalid insert values type %s", tb.WriteNode(insertRows).String()) + return + } +} + +func (r *Resolver) buildSelectExpr(dbID string, selectExpr sqlparser.SelectExpr, hasDistinct bool, originColumns Columns) (cols Columns, err error) { + var tempCols Columns + switch e := selectExpr.(type) { + case *sqlparser.AliasedExpr: + if tempCols, err = r.buildExpression(dbID, e.Expr, originColumns); err != nil { + return + } + + // must be one column result + if len(tempCols) != 1 { + err = errors.Wrapf(ErrInvalidStatement, "sub query returns %d columns", len(tempCols)) + return + } + + if e.As.IsEmpty() { + // no alias, keep as the original column result + cols = append(cols, tempCols[0]) + return + } + + // add alias process + cols = append(cols, &Column{ + ColName: sqlparser.ColName{ + Name: sqlparser.NewColIdent(e.As.String()), + }, + IsPhysical: false, + Computation: &Computation{ + Op: "alias", + Operands: Columns{tempCols[0]}, + }, + }) + case *sqlparser.StarExpr: + // filter origin columns + if e.TableName.IsEmpty() { + cols = append(cols, originColumns...) + } else { + // filter by table name + starColumns := make(Columns, 0, len(originColumns)) + + for _, c := range originColumns { + if strings.EqualFold(e.TableName.Name.String(), c.ColName.Qualifier.Name.String()) { + starColumns = append(starColumns, c) + } + } + + if len(starColumns) == 0 { + err = errors.Wrapf(ErrInvalidTable, "no such table %s", e.TableName.Name.String()) + return + } + + cols = append(cols, starColumns...) + } + } + + cols = func() Columns { + newCols := NewColumns() + for _, c := range cols { + newCols = append(newCols, &Column{ + ColName: c.ColName, + IsPhysical: false, + Computation: &Computation{ + Op: "distinct", + Operands: cols, + }, + }) + } + return newCols + }() + + return +} + +func (r *Resolver) buildSelectExprs(dbID string, selectExprs sqlparser.SelectExprs, hasDistinct bool, originColumns Columns) (cols Columns, err error) { + var tempCols Columns + for _, expr := range selectExprs { + if tempCols, err = r.buildSelectExpr(dbID, expr, hasDistinct, originColumns); err != nil { + return + } + cols = append(cols, tempCols...) + } + + return +} + +func (r *Resolver) filterDependentCols(originColumns Columns) (cols Columns) { + cols = make(Columns, 0, len(originColumns)) + + for _, c := range originColumns { + if !c.IsPhysical { + // do not add dependency that only contains aliases + if c.Computation != nil && c.Computation.Op == "alias" { + tempCols := r.filterDependentCols(c.Computation.Operands) + if len(tempCols) == 0 { + continue + } + } + cols = append(cols, c) + } + } + + return +} + +func (r *Resolver) buildSelectStatement(dbID string, stmt sqlparser.SelectStatement, extraColumns Columns) (cols Columns, err error) { + var ( + tempCols Columns + originCols Columns + ) + + switch n := stmt.(type) { + case *sqlparser.Select: + var ( + resultCols Columns + symTableCols = NewColumns(extraColumns...) + ) + // in select origin cols may not be physical cols, physical cols should be filtered + if originCols, err = r.buildAllColumnInTableExprs(dbID, n.From, extraColumns); err != nil { + return + } + // append all virtual fields to dependent cols + tempCols = r.filterDependentCols(originCols) + cols = append(cols, tempCols...) + // use all physical table columns as symbol table columns + symTableCols = append(symTableCols, originCols...) + // add extra columns from outer symbol table + for _, c := range extraColumns { + if _, err := symTableCols.ResolveColName(&c.ColName); err != nil { + symTableCols = append(symTableCols, c) + } + } + // select expression is the projection of origin cols + if resultCols, err = r.buildSelectExprs(dbID, n.SelectExprs, n.Distinct != "", originCols); err != nil { + return + } + cols = append(cols, resultCols...) + // build sym table cols + for _, c := range resultCols { + if _, err := symTableCols.ResolveColIdent(c.ColName.Name); err != nil { + // not exists + symTableCols = append(symTableCols, c) + } + } + if tempCols, err = r.buildWhere(dbID, n.Where, symTableCols); err != nil { + return + } + cols = append(cols, tempCols...) + if tempCols, err = r.buildGroupBy(dbID, n.GroupBy, symTableCols); err != nil { + return + } + cols = append(cols, tempCols...) + if tempCols, err = r.buildWhere(dbID, n.Having, symTableCols); err != nil { + return + } + cols = append(cols, tempCols...) + if tempCols, err = r.buildOrderBy(dbID, n.OrderBy, symTableCols); err != nil { + return + } + cols = append(cols, tempCols...) + if tempCols, err = r.buildLimit(dbID, n.Limit); err != nil { + return + } + cols = append(cols, tempCols...) + // iterate result cols + cols = func() Columns { + newCols := NewColumns() + for _, c := range resultCols { + newCols = append(newCols, &Column{ + ColName: c.ColName, + IsPhysical: false, + Computation: &Computation{ + Op: "filter", + Operands: cols, + }, + }) + } + return newCols + }() + case *sqlparser.Union: + if originCols, err = r.buildSelectStatement(dbID, n.Left, nil); err != nil { + return + } + cols = append(cols, originCols...) + if tempCols, err = r.buildSelectStatement(dbID, n.Right, nil); err != nil { + return + } + cols = append(cols, tempCols...) + if tempCols, err = r.buildOrderBy(dbID, n.OrderBy, cols); err != nil { + return + } + cols = append(cols, tempCols...) + if tempCols, err = r.buildLimit(dbID, n.Limit); err != nil { + return + } + cols = append(cols, tempCols...) + cols = func() Columns { + newCols := NewColumns() + for _, c := range originCols { + newCols = append(newCols, &Column{ + ColName: c.ColName, + IsPhysical: false, + Computation: &Computation{ + Op: "union", + Operands: cols, + }, + }) + } + return newCols + }() + case *sqlparser.ParenSelect: + // not supported in sqlite + err = errors.Wrap(ErrInvalidStatement, "invalid paren select statement") + } + return +} + +func (r *Resolver) GetDependentPhysicalColumns(dbID string, stmt sqlparser.Statement) (columns []*ColumnResult, err error) { + var ( + cols Columns + columnMap = make(map[string]map[string]int) + visit func(opStack []string, cols Columns) + ) + columns = make([]*ColumnResult, 0) + if cols, err = r.BuildPhysicalColumnsTransformations(dbID, stmt); err != nil { + return + } + visit = func(opStack []string, cols Columns) { + for _, c := range cols { + if !c.IsPhysical { + if c.Computation != nil { + subStack := make([]string, len(opStack)+1) + subStack[0] = c.Computation.Op + copy(subStack[1:], opStack) + visit(subStack, c.Computation.Operands) + } + } else { + tableName := strings.ToLower(c.ColName.Qualifier.Name.String()) + colName := c.ColName.Name.Lowered() + + if o, exists := columnMap[tableName][colName]; !exists { + if _, exists := columnMap[tableName]; !exists { + columnMap[tableName] = make(map[string]int) + } + + columnMap[tableName][colName] = len(columns) + columns = append(columns, &ColumnResult{ + TableName: tableName, + ColName: colName, + Ops: [][]string{opStack}, + }) + } else { + // exists + columns[o].Ops = append(columns[o].Ops, opStack) + } + } + } + } + visit(nil, cols) + return +} + +func (r *Resolver) BuildPhysicalColumnsTransformations(dbID string, stmt sqlparser.Statement) (cols Columns, err error) { + var ( + tempCols Columns + originCols Columns + ) + + switch n := stmt.(type) { + case sqlparser.SelectStatement: + return r.buildSelectStatement(dbID, n, nil) + case *sqlparser.Insert: + // in insert, origin cols ia always physical cols + if originCols, err = r.buildAllColumnInTable(dbID, n.Table); err != nil { + return + } + // insert columns can be projected + if tempCols, err = r.buildInsertColumns(dbID, n.Columns, originCols); err != nil { + return + } + cols = append(cols, tempCols...) + // insert values has no relation to the table + if tempCols, err = r.buildInsertRows(dbID, n.Rows); err != nil { + return + } + cols = append(cols, tempCols...) + // update expression can be projected, on duplication expression is the same as update expression + if tempCols, err = r.buildUpdateExprs(dbID, sqlparser.UpdateExprs(n.OnDup), originCols); err != nil { + return + } + cols = append(cols, tempCols...) + case *sqlparser.Update: + // in update, origin cols is always physical cols + if originCols, err = r.buildAllColumnInTableExprs(dbID, n.TableExprs, nil); err != nil { + return + } + // update expression can be projected + if tempCols, err = r.buildUpdateExprs(dbID, n.Exprs, originCols); err != nil { + return + } + cols = append(cols, tempCols...) + if tempCols, err = r.buildWhere(dbID, n.Where, originCols); err != nil { + return + } + cols = append(cols, tempCols...) + if tempCols, err = r.buildOrderBy(dbID, n.OrderBy, originCols); err != nil { + return + } + cols = append(cols, tempCols...) + if tempCols, err = r.buildLimit(dbID, n.Limit); err != nil { + return + } + cols = append(cols, tempCols...) + case *sqlparser.Delete: + if originCols, err = r.buildAllColumnInTableExprs(dbID, n.TableExprs, nil); err != nil { + return + } + cols = append(cols, originCols...) + if tempCols, err = r.buildWhere(dbID, n.Where, originCols); err != nil { + return + } + cols = append(cols, tempCols...) + if tempCols, err = r.buildOrderBy(dbID, n.OrderBy, originCols); err != nil { + return + } + cols = append(cols, tempCols...) + if tempCols, err = r.buildLimit(dbID, n.Limit); err != nil { + return + } + cols = append(cols, tempCols...) + case *sqlparser.DDL: + // requires table privilege, if create table statement is passed, requires extra privilege + case *sqlparser.Show: + // no physical column rely, but requires extra privilege + case *sqlparser.Explain: + // no physical column rely, but requires extra privilege + default: + // invalid statement + err = errors.Wrap(ErrInvalidStatement, "unknown statement") + } + + return +} diff --git a/cmd/cql-secure-gateway/resolver_test.go b/cmd/cql-secure-gateway/resolver_test.go new file mode 100644 index 000000000..544fbde40 --- /dev/null +++ b/cmd/cql-secure-gateway/resolver_test.go @@ -0,0 +1,251 @@ +/* + * Copyright 2018 The CovenantSQL Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package main + +import ( + "github.com/CovenantSQL/CovenantSQL/cmd/cql-mysql-adapter/resolver" + "github.com/CovenantSQL/sqlparser" + . "github.com/smartystreets/goconvey/convey" + "testing" +) + +func newColName(table string, col string) sqlparser.ColName { + return sqlparser.ColName{ + Qualifier: sqlparser.TableName{ + Name: sqlparser.NewTableIdent(table), + }, + Name: sqlparser.NewColIdent(col), + } +} + +func mustParseStatement(c C, query string) sqlparser.Statement { + stmt, err := sqlparser.Parse(query) + c.So(err, ShouldBeNil) + return stmt +} + +func TestResolverColumns(t *testing.T) { + Convey("test resolve column names", t, func() { + cols := NewColumns( + &Column{ + ColName: newColName("test", "test"), + IsPhysical: true, + }, + &Column{ + ColName: newColName("test2", "test"), + IsPhysical: true, + }, + &Column{ + ColName: newColName("test", "test3"), + IsPhysical: true, + }, + ) + + col1 := newColName("test", "test") + col, err := cols.ResolveColName(&col1) + So(err, ShouldBeNil) + So(col, ShouldNotBeNil) + So(col1.Qualifier.Name.String(), ShouldEqual, "test") + So(col1.Name.String(), ShouldEqual, "test") + + col2 := newColName("test4", "test") + _, err = cols.ResolveColName(&col2) + So(err, ShouldNotBeNil) + + col3 := newColName("test", "test2") + _, err = cols.ResolveColName(&col3) + So(err, ShouldNotBeNil) + + col4 := newColName("", "test") + _, err = cols.ResolveColName(&col4) + So(err, ShouldNotBeNil) + + col, err = cols.ResolveColName(nil) + So(col, ShouldBeNil) + So(err, ShouldBeNil) + + col, err = cols.ResolveColIdent(sqlparser.NewColIdent("test3")) + So(err, ShouldBeNil) + So(col, ShouldNotBeNil) + + _, err = cols.ResolveColIdent(sqlparser.NewColIdent("test2")) + So(err, ShouldNotBeNil) + }) +} + +func TestColumnResolver(t *testing.T) { + Convey("resolve columns", t, func(c C) { + var ( + r = &Resolver{Resolver: resolver.NewResolver()} + dbID = randomDBID() + cols Columns + err error + db *wrapFakeDB + colResults []*ColumnResult + ) + + // inject normal database + db, err = fakeConn([]string{ + "CREATE TABLE t1(a INTEGER, b INTEGER, c INTEGER, d INTEGER, e INTEGER)", + "CREATE TABLE tab0(col0 INTEGER, col1 INTEGER, col2 INTEGER)", + "CREATE TABLE tab1(col0 INTEGER, col1 INTEGER, col2 INTEGER)", + "CREATE TABLE tab2(col0 INTEGER, col1 INTEGER, col2 INTEGER)", + "CREATE TABLE tab3(pk INTEGER PRIMARY KEY, col0 INTEGER, col1 FLOAT, col2 TEXT, col3 INTEGER, col4 FLOAT, col5 TEXT)", + }) + So(err, ShouldBeNil) + r.RegisterDB(dbID, db) + defer db.Close() + + cols, err = r.BuildPhysicalColumnsTransformations(dbID, mustParseStatement(c, "SELECT ? AS B")) + So(err, ShouldBeNil) + So(cols, ShouldHaveLength, 1) + + cols, err = r.BuildPhysicalColumnsTransformations(dbID, mustParseStatement(c, "SELECT ? AS A, ? AS B")) + So(err, ShouldBeNil) + So(cols, ShouldHaveLength, 2) + + cols, err = r.BuildPhysicalColumnsTransformations(dbID, mustParseStatement(c, "SELECT test FROM (SELECT 1 AS test)")) + So(err, ShouldBeNil) + So(cols, ShouldHaveLength, 1) + + cols, err = r.BuildPhysicalColumnsTransformations(dbID, mustParseStatement(c, "SELECT * FROM (SELECT 1 AS test)")) + So(err, ShouldBeNil) + So(cols, ShouldHaveLength, 1) + + cols, err = r.BuildPhysicalColumnsTransformations(dbID, mustParseStatement(c, "SELECT t.* FROM (SELECT 1 AS test) AS t")) + So(err, ShouldBeNil) + So(cols, ShouldHaveLength, 1) + + cols, err = r.BuildPhysicalColumnsTransformations(dbID, mustParseStatement(c, "SELECT test FROM (SELECT 1 AS test) AS t")) + So(err, ShouldBeNil) + So(cols, ShouldHaveLength, 1) + + cols, err = r.BuildPhysicalColumnsTransformations(dbID, mustParseStatement(c, "SELECT * FROM (SELECT 1 AS test1), (SELECT 1 AS test2)")) + So(err, ShouldBeNil) + So(cols, ShouldHaveLength, 2) + + cols, err = r.BuildPhysicalColumnsTransformations(dbID, mustParseStatement(c, "SELECT t1.* FROM (SELECT 1 AS test1) AS t1 JOIN (SELECT 1 AS test2) AS t2")) + So(err, ShouldBeNil) + So(cols, ShouldHaveLength, 1) + + cols, err = r.BuildPhysicalColumnsTransformations(dbID, mustParseStatement(c, "SELECT * FROM (SELECT 1 AS test1) AS t1 JOIN (SELECT 1 AS test2) AS t2")) + So(err, ShouldBeNil) + So(cols, ShouldHaveLength, 2) + + cols, err = r.BuildPhysicalColumnsTransformations(dbID, mustParseStatement(c, "SELECT 1 AS test UNION SELECT 2 AS test")) + So(err, ShouldBeNil) + So(cols, ShouldHaveLength, 1) + + cols, err = r.BuildPhysicalColumnsTransformations(dbID, mustParseStatement(c, ` + SELECT (a+b+c+d+e)/5, + a+b*2+c*3+d*4+e*5, + d-e, + a+b*2, + CASE a+1 WHEN b THEN 111 WHEN c THEN 222 + WHEN d THEN 333 WHEN e THEN 444 ELSE 555 END, + c + FROM t1 + WHERE c BETWEEN b-2 AND d+2 + AND b>c + AND a>b`)) + So(err, ShouldBeNil) + So(cols, ShouldHaveLength, 6) + + cols, err = r.BuildPhysicalColumnsTransformations(dbID, mustParseStatement(c, ` + SELECT CASE a+1 WHEN b THEN 111 WHEN c THEN 222 + WHEN d THEN 333 WHEN e THEN 444 ELSE 555 END + FROM t1 + WHERE EXISTS(SELECT 1 FROM t1 AS x WHERE x.b=d+2) + AND e+d BETWEEN a+b-10 AND c+130 + AND b IS NOT NULL`)) + So(err, ShouldBeNil) + So(cols, ShouldHaveLength, 2) + + colResults, err = r.GetDependentPhysicalColumns(dbID, mustParseStatement(c, ` +SELECT c-d, + abs(b-c) + FROM t1 + WHERE (c<=d-2 OR c>=d+2) + AND e+d BETWEEN a+b-10 AND c+130 + AND b IS NOT NULL`)) + So(err, ShouldBeNil) + So(colResults, ShouldHaveLength, 5) + + colResults, err = r.GetDependentPhysicalColumns(dbID, mustParseStatement(c, ` +SELECT a-b, + (SELECT count(1) FROM t1 AS x WHERE x.ba AND e 901.6) AND col0 >= 17 AND (col0 IS NULL) AND col0 < 649) AND (col3 > 960) OR (col3 > 58) ORDER BY 1 DESC`)) + So(err, ShouldBeNil) + So(colResults, ShouldHaveLength, 4) + + colResults, err = r.GetDependentPhysicalColumns(dbID, mustParseStatement(c, ` +SELECT substr(pk, 1, 1), group_concat(pk), cast(pk as string) FROM tab3 +`)) + So(err, ShouldBeNil) + So(colResults, ShouldHaveLength, 1) + + colResults, err = r.GetDependentPhysicalColumns(dbID, mustParseStatement(c, ` +SELECT true, null, current_timestamp, not false +`)) + So(err, ShouldBeNil) + So(colResults, ShouldHaveLength, 0) + + colResults, err = r.GetDependentPhysicalColumns(dbID, mustParseStatement(c, ` +SELECT col0 FROM tab0, tab1 +`)) + So(err, ShouldNotBeNil) + + colResults, err = r.GetDependentPhysicalColumns(dbID, mustParseStatement(c, ` +SELECT col1024 FROM tab0 +`)) + So(err, ShouldNotBeNil) + }) +} diff --git a/cmd/cql-secure-gateway/xxx_test.go b/cmd/cql-secure-gateway/xxx_test.go new file mode 100644 index 000000000..6f0c93df4 --- /dev/null +++ b/cmd/cql-secure-gateway/xxx_test.go @@ -0,0 +1,81 @@ +/* + * Copyright 2018 The CovenantSQL Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package main + +import ( + "crypto/rand" + "database/sql" + "github.com/CovenantSQL/CovenantSQL/crypto/hash" + _ "github.com/CovenantSQL/go-sqlite3-encrypt" + "github.com/CovenantSQL/sqlparser" + "github.com/pkg/errors" +) + +type wrapFakeDB struct { + *sql.DB +} + +func (d *wrapFakeDB) Query(query string, args ...interface{}) (rows *sql.Rows, err error) { + var ( + stmt sqlparser.Statement + showStmt *sqlparser.Show + ok bool + ) + if stmt, err = sqlparser.Parse(query); err != nil { + return + } + + if showStmt, ok = stmt.(*sqlparser.Show); !ok { + err = errors.New("invalid query") + return + } + + switch showStmt.Type { + case "tables": + return d.DB.Query("SELECT name FROM sqlite_master WHERE type = \"table\"") + case "table": + return d.DB.Query("PRAGMA table_info(" + showStmt.OnTable.Name.String() + ")") + default: + err = errors.New("invalid query") + return + } +} + +func fakeConn(testCreateTables []string) (db *wrapFakeDB, err error) { + var realDB *sql.DB + if realDB, err = sql.Open("sqlite3", ":memory:"); err != nil { + return + } + + for _, q := range testCreateTables { + if _, err = realDB.Exec(q); err != nil { + return + } + } + + db = &wrapFakeDB{ + DB: realDB, + } + + return +} + +func randomDBID() string { + h := hash.Hash{} + rand.Read(h[:]) + return h.String() +} From 555b6ed0152f75309b125bfee12dd041721318d4 Mon Sep 17 00:00:00 2001 From: Qi Xiao Date: Tue, 27 Nov 2018 10:16:32 +0800 Subject: [PATCH 14/24] Update deps --- Gopkg.lock | 30 +- .../github.com/CovenantSQL/sqlparser/ast.go | 61 - .../github.com/CovenantSQL/sqlparser/sql.go | 2136 ++++++++--------- vendor/github.com/CovenantSQL/sqlparser/sql.y | 10 - .../github.com/CovenantSQL/sqlparser/token.go | 1 - .../btcsuite/btcd/btcec/signature.go | 4 +- .../client_golang/prometheus/desc.go | 4 +- .../syndtr/goleveldb/leveldb/opt/options.go | 13 + .../syndtr/goleveldb/leveldb/table.go | 24 +- .../golang.org/x/crypto/ssh/terminal/util.go | 4 +- .../x/crypto/ssh/terminal/util_aix.go | 12 + .../x/crypto/ssh/terminal/util_plan9.go | 2 +- .../x/crypto/ssh/terminal/util_solaris.go | 2 +- .../x/crypto/ssh/terminal/util_windows.go | 2 +- vendor/golang.org/x/sys/unix/syscall_linux.go | 9 + 15 files changed, 1125 insertions(+), 1189 deletions(-) create mode 100644 vendor/golang.org/x/crypto/ssh/terminal/util_aix.go diff --git a/Gopkg.lock b/Gopkg.lock index aabe4fbc8..ee0c26d53 100644 --- a/Gopkg.lock +++ b/Gopkg.lock @@ -20,7 +20,7 @@ name = "github.com/CovenantSQL/HashStablePack" packages = ["marshalhash"] pruneopts = "UT" - revision = "f5d7cc3bf3356c85eadcb0f66007f2f2b7ee81bc" + revision = "ea383e6c5f56c8db09130378d7eaf1631d14ecc0" [[projects]] branch = "develop" @@ -32,7 +32,7 @@ [[projects]] branch = "master" - digest = "1:baa85b6c43f7fa2d05914718fda404f6ce9a63aed243809c4090326730f0ecd8" + digest = "1:519e30d8e2f43fcde79a23993cd4e8fe609841d8e7666cc26f8dff0d2613f2c4" name = "github.com/CovenantSQL/sqlparser" packages = [ ".", @@ -42,7 +42,7 @@ "dependency/sqltypes", ] pruneopts = "UT" - revision = "d9be292d3627ca3e62ec927e73b34a09b0b0b06d" + revision = "765b8d4e9426de3b49bfb96e520093504fb0d509" [[projects]] branch = "master" @@ -118,11 +118,11 @@ [[projects]] branch = "master" - digest = "1:c0decf632843204d2b8781de7b26e7038584e2dcccc7e2f401e88ae85b1df2b7" + digest = "1:093bf93a65962e8191e3e8cd8fc6c363f83d43caca9739c906531ba7210a9904" name = "github.com/btcsuite/btcd" packages = ["btcec"] pruneopts = "UT" - revision = "67e573d211ace594f1366b4ce9d39726c4b19bd0" + revision = "3dcf298fed2d5fd65918dc560b3942b2aa0629e8" [[projects]] branch = "master" @@ -372,14 +372,14 @@ [[projects]] branch = "master" - digest = "1:735956b5e94182229dfbdc5b401d7a71f20f54f1259dbc2969ec2b56bfbb34f2" + digest = "1:60550d27cee0a4d6f3ef35c169b9f0cf596636f77df11e2acea8626ce76d959c" name = "github.com/prometheus/client_golang" packages = [ "prometheus", "prometheus/internal", ] pruneopts = "UT" - revision = "3fb53dff765f8a3e0f9d8b1d5b86d4f8c4eb3a09" + revision = "32b1bb4674c4db541df5b547bb1a325337522022" [[projects]] branch = "master" @@ -400,7 +400,7 @@ "version", ] pruneopts = "UT" - revision = "b36ad289a3eaecdc52470a19591146a2c0ffb532" + revision = "4724e9255275ce38f7179b2478abeae4e28c904f" [[projects]] branch = "master" @@ -413,7 +413,7 @@ "xfs", ] pruneopts = "UT" - revision = "185b4288413d2a0dd0806f78c90dde719829e5ae" + revision = "619930b0b4713cc1280189bf0a4c54b3fb506f60" [[projects]] branch = "master" @@ -500,7 +500,7 @@ [[projects]] branch = "master" - digest = "1:9ff9e1808adfc43f788f1c1e9fd2660c285b522243da985a4c043ec6f2a2d736" + digest = "1:2c4379920afd63d262751932c18dfaa30d92ee8241403a919cd7a3e4cf9561d6" name = "github.com/syndtr/goleveldb" packages = [ "leveldb", @@ -517,7 +517,7 @@ "leveldb/util", ] pruneopts = "UT" - revision = "f9080354173f192dfc8821931eacf9cfd6819253" + revision = "d5f8e13ca70d404acb9f9f8daab06b3818425145" [[projects]] digest = "1:d9eb1d964a449cf51455fdac2ec3451c664af8e8b93c23d9c31d22b84ea82fde" @@ -603,7 +603,7 @@ [[projects]] branch = "master" - digest = "1:583f2f436bab7b59a7bd3e759f1375b06f460760ed1f9235604d143eaab83009" + digest = "1:cfd661f1a52594117f2a753bb640a86d4dbf3e0d778c2641bfbc750e6a1c8be7" name = "golang.org/x/crypto" packages = [ "ed25519", @@ -611,7 +611,7 @@ "ssh/terminal", ] pruneopts = "UT" - revision = "3d3f9f413869b949e48070b5bc593aa22cc2b8f2" + revision = "e657309f52e71501f9934566ac06dc5c2f7f11a1" [[projects]] branch = "master" @@ -632,14 +632,14 @@ [[projects]] branch = "master" - digest = "1:48b757e61d662021f49a6a0afc67dacae27ee0c3a8f858d927126bcdf49222d3" + digest = "1:f343f077a5b0bc3a3788b3a04e24dd417e3e25b2acb529c413e212d2c42416ef" name = "golang.org/x/sys" packages = [ "unix", "windows", ] pruneopts = "UT" - revision = "ec83556a53fe16b65c452a104ea9d1e86a671852" + revision = "62eef0e2fa9b2c385f7b2778e763486da6880d37" [[projects]] digest = "1:342378ac4dcb378a5448dd723f0784ae519383532f5e70ade24132c4c8693202" diff --git a/vendor/github.com/CovenantSQL/sqlparser/ast.go b/vendor/github.com/CovenantSQL/sqlparser/ast.go index eb3735cc7..7ccdc87ba 100644 --- a/vendor/github.com/CovenantSQL/sqlparser/ast.go +++ b/vendor/github.com/CovenantSQL/sqlparser/ast.go @@ -1790,8 +1790,6 @@ func (*FuncExpr) iExpr() {} func (*CaseExpr) iExpr() {} func (*ValuesFuncExpr) iExpr() {} func (*ConvertExpr) iExpr() {} -func (*SubstrExpr) iExpr() {} -func (*ConvertUsingExpr) iExpr() {} func (*MatchExpr) iExpr() {} func (*GroupConcatExpr) iExpr() {} func (*Default) iExpr() {} @@ -2639,40 +2637,6 @@ func (node *ValuesFuncExpr) replace(from, to Expr) bool { return false } -// SubstrExpr represents a call to SubstrExpr(column, value_expression) or SubstrExpr(column, value_expression,value_expression) -// also supported syntax SubstrExpr(column from value_expression for value_expression) -type SubstrExpr struct { - Name *ColName - From Expr - To Expr -} - -// Format formats the node. -func (node *SubstrExpr) Format(buf *TrackedBuffer) { - - if node.To == nil { - buf.Myprintf("substr(%v, %v)", node.Name, node.From) - } else { - buf.Myprintf("substr(%v, %v, %v)", node.Name, node.From, node.To) - } -} - -func (node *SubstrExpr) replace(from, to Expr) bool { - return replaceExprs(from, to, &node.From, &node.To) -} - -func (node *SubstrExpr) walkSubtree(visit Visit) error { - if node == nil { - return nil - } - return Walk( - visit, - node.Name, - node.From, - node.To, - ) -} - // ConvertExpr represents a call to CONVERT(expr, type) // or it's equivalent CAST(expr AS type). Both are rewritten to the former. type ConvertExpr struct { @@ -2700,31 +2664,6 @@ func (node *ConvertExpr) replace(from, to Expr) bool { return replaceExprs(from, to, &node.Expr) } -// ConvertUsingExpr represents a call to CONVERT(expr USING charset). -type ConvertUsingExpr struct { - Expr Expr - Type string -} - -// Format formats the node. -func (node *ConvertUsingExpr) Format(buf *TrackedBuffer) { - buf.Myprintf("convert(%v using %s)", node.Expr, node.Type) -} - -func (node *ConvertUsingExpr) walkSubtree(visit Visit) error { - if node == nil { - return nil - } - return Walk( - visit, - node.Expr, - ) -} - -func (node *ConvertUsingExpr) replace(from, to Expr) bool { - return replaceExprs(from, to, &node.Expr) -} - // ConvertType represents the type in call to CONVERT(expr, type) type ConvertType struct { Type string diff --git a/vendor/github.com/CovenantSQL/sqlparser/sql.go b/vendor/github.com/CovenantSQL/sqlparser/sql.go index 2240f95b0..7a3cde97d 100644 --- a/vendor/github.com/CovenantSQL/sqlparser/sql.go +++ b/vendor/github.com/CovenantSQL/sqlparser/sql.go @@ -218,10 +218,9 @@ const CURRENT_DATE = 57472 const CURRENT_TIME = 57473 const REPLACE = 57474 const CAST = 57475 -const SUBSTR = 57476 -const GROUP_CONCAT = 57477 -const SEPARATOR = 57478 -const UNUSED = 57479 +const GROUP_CONCAT = 57476 +const SEPARATOR = 57477 +const UNUSED = 57478 var yyToknames = [...]string{ "$end", @@ -374,7 +373,6 @@ var yyToknames = [...]string{ "CURRENT_TIME", "REPLACE", "CAST", - "SUBSTR", "GROUP_CONCAT", "SEPARATOR", "UNUSED", @@ -412,552 +410,542 @@ var yyExca = [...]int{ 38, 157, 40, 157, 41, 157, - 155, 157, + 154, 157, -2, 170, + -1, 184, + 92, 355, + -2, 351, -1, 185, - 92, 357, - -2, 353, - -1, 186, + 92, 356, + -2, 352, + -1, 402, + 5, 17, + -2, 324, + -1, 519, 92, 358, -2, 354, - -1, 405, - 5, 17, - -2, 326, - -1, 522, - 92, 360, - -2, 356, - -1, 554, + -1, 550, 5, 18, -2, 229, - -1, 612, + -1, 607, 5, 18, - -2, 327, - -1, 675, + -2, 325, + -1, 669, 5, 17, - -2, 329, - -1, 735, + -2, 327, + -1, 726, 5, 18, - -2, 330, + -2, 328, } const yyPrivate = 57344 -const yyLast = 4188 +const yyLast = 4100 var yyAct = [...]int{ - 186, 384, 626, 138, 537, 462, 32, 538, 172, 517, - 409, 432, 144, 101, 590, 431, 492, 437, 529, 47, - 208, 521, 170, 95, 95, 338, 443, 121, 477, 549, - 188, 467, 408, 433, 209, 3, 458, 212, 451, 95, - 31, 45, 199, 136, 428, 149, 718, 140, 113, 134, - 631, 632, 633, 569, 450, 112, 519, 634, 630, 111, - 124, 99, 355, 354, 364, 365, 357, 358, 359, 360, - 361, 362, 363, 356, 708, 706, 340, 469, 468, 109, - 445, 125, 468, 687, 128, 95, 641, 85, 86, 445, - 95, 123, 722, 91, 260, 130, 591, 122, 255, 254, - 25, 27, 15, 16, 644, 84, 563, 615, 597, 95, - 547, 190, 151, 481, 383, 135, 356, 22, 95, 95, - 95, 173, 26, 127, 344, 95, 546, 670, 95, 530, - 95, 95, 87, 29, 343, 342, 95, 89, 88, 133, - 342, 191, 359, 360, 361, 362, 363, 356, 26, 253, - 344, 116, 635, 264, 444, 344, 106, 262, 442, 441, - 117, 688, 686, 444, 100, 377, 378, 379, 380, 381, - 357, 358, 359, 360, 361, 362, 363, 356, 567, 141, - 189, 398, 399, 530, 251, 604, 346, 18, 19, 20, - 485, 487, 488, 337, 95, 401, 195, 486, 95, 95, - 95, 95, 21, 23, 400, 690, 24, 95, 499, 345, - 129, 95, 424, 425, 95, 343, 342, 447, 47, 411, - 347, 497, 498, 496, 448, 343, 342, 405, 26, 734, - 116, 344, 689, 572, 599, 583, 584, 585, 571, 210, - 436, 344, 410, 28, 570, 426, 438, 414, 422, 416, - 385, 427, 628, 413, 391, 415, 464, 723, 724, 375, - 122, 95, 95, 564, 453, 454, 515, 374, 376, 465, - 343, 342, 343, 342, 139, 455, 456, 457, 729, 672, - 29, 728, 683, 682, 460, 461, 344, 470, 344, 636, - 495, 523, 382, 474, 139, 386, 387, 388, 389, 429, - 392, 639, 638, 610, 393, 394, 395, 614, 139, 29, - 116, 116, 116, 116, 406, 523, 139, 197, 139, 117, - 117, 117, 117, 116, 218, 217, 197, 350, 545, 353, - 475, 637, 210, 33, 545, 366, 367, 368, 369, 370, - 371, 47, 351, 352, 349, 372, 355, 354, 364, 365, - 357, 358, 359, 360, 361, 362, 363, 356, 493, 474, - 474, 491, 600, 479, 500, 501, 502, 503, 504, 505, - 506, 507, 508, 509, 510, 511, 512, 513, 514, 474, - 557, 25, 47, 25, 396, 25, 452, 463, 524, 355, - 354, 364, 365, 357, 358, 359, 360, 361, 362, 363, - 356, 522, 403, 404, 516, 674, 436, 47, 621, 560, - 459, 411, 196, 539, 29, 411, 29, 520, 29, 482, - 483, 426, 489, 490, 573, 197, 411, 535, 41, 543, - 554, 12, 47, 527, 526, 555, 536, 553, 531, 550, - 551, 541, 419, 558, 139, 552, 420, 421, 204, 410, - 532, 533, 534, 544, 436, 201, 204, 418, 202, 417, - 203, 205, 494, 385, 525, 566, 105, 727, 726, 562, - 95, 561, 466, 189, 257, 207, 95, 137, 107, 47, - 355, 354, 364, 365, 357, 358, 359, 360, 361, 362, - 363, 356, 102, 411, 697, 574, 579, 103, 26, 522, - 578, 479, 33, 582, 436, 493, 556, 580, 386, 696, - 587, 588, 589, 586, 643, 520, 576, 36, 577, 593, - 429, 594, 261, 131, 201, 204, 540, 202, 26, 203, - 205, 550, 551, 38, 39, 606, 607, 608, 609, 119, - 35, 37, 97, 98, 611, 612, 613, 603, 30, 1, - 90, 616, 625, 440, 430, 120, 617, 40, 47, 439, - 47, 47, 685, 629, 446, 522, 568, 449, 411, 622, - 436, 624, 565, 623, 95, 595, 221, 222, 220, 224, - 436, 619, 436, 627, 601, 223, 219, 652, 116, 691, - 206, 410, 211, 476, 42, 373, 108, 117, 47, 649, - 598, 680, 645, 542, 647, 648, 650, 605, 669, 494, - 397, 187, 695, 668, 642, 47, 47, 385, 673, 539, - 436, 602, 47, 618, 47, 390, 528, 620, 150, 484, - 679, 651, 160, 157, 159, 558, 158, 677, 678, 147, - 402, 47, 596, 348, 681, 47, 436, 148, 675, 142, - 115, 192, 171, 200, 700, 198, 701, 548, 114, 699, - 473, 717, 704, 436, 411, 14, 684, 436, 34, 110, - 11, 10, 715, 17, 693, 93, 93, 9, 651, 720, - 8, 671, 7, 539, 47, 6, 725, 694, 5, 4, - 104, 93, 702, 13, 703, 2, 705, 0, 707, 731, - 709, 710, 711, 712, 713, 714, 627, 118, 0, 0, - 0, 721, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 733, 0, 47, 47, 735, 0, 0, 0, 732, - 92, 96, 540, 0, 0, 676, 738, 93, 0, 0, - 0, 667, 93, 0, 0, 736, 737, 0, 0, 719, - 354, 364, 365, 357, 358, 359, 360, 361, 362, 363, - 356, 93, 692, 0, 0, 0, 0, 0, 0, 0, - 93, 214, 93, 730, 385, 0, 0, 93, 0, 0, - 93, 0, 93, 93, 0, 0, 0, 0, 263, 341, - 0, 0, 126, 0, 0, 0, 540, 132, 26, 0, - 0, 0, 0, 0, 0, 0, 0, 654, 0, 0, - 663, 664, 665, 662, 0, 666, 194, 0, 0, 0, - 656, 0, 659, 661, 655, 0, 653, 216, 0, 657, - 716, 139, 252, 0, 0, 256, 0, 258, 259, 658, - 660, 0, 0, 0, 0, 0, 93, 0, 0, 412, - 93, 93, 93, 93, 0, 0, 0, 0, 0, 423, - 646, 0, 0, 93, 0, 0, 214, 355, 354, 364, - 365, 357, 358, 359, 360, 361, 362, 363, 356, 0, - 355, 354, 364, 365, 357, 358, 359, 360, 361, 362, - 363, 356, 0, 68, 0, 518, 0, 146, 0, 0, - 0, 407, 145, 0, 178, 60, 0, 0, 0, 0, - 0, 0, 0, 93, 93, 29, 0, 0, 185, 162, - 161, 163, 164, 165, 166, 592, 0, 167, 168, 169, - 0, 0, 0, 0, 143, 155, 0, 177, 0, 0, - 0, 0, 0, 0, 0, 355, 354, 364, 365, 357, - 358, 359, 360, 361, 362, 363, 356, 152, 153, 339, - 0, 0, 0, 183, 0, 154, 0, 156, 471, 472, - 0, 0, 0, 0, 0, 0, 0, 0, 182, 0, - 69, 0, 0, 57, 0, 0, 52, 0, 0, 77, + 185, 621, 533, 338, 382, 429, 172, 534, 514, 459, + 428, 170, 525, 489, 518, 474, 586, 208, 3, 47, + 406, 207, 440, 95, 95, 425, 101, 136, 32, 336, + 45, 545, 118, 121, 405, 464, 434, 430, 187, 95, + 455, 113, 211, 31, 99, 138, 149, 198, 173, 26, + 710, 626, 627, 628, 140, 92, 96, 565, 629, 134, + 111, 447, 625, 124, 702, 151, 700, 681, 466, 112, + 465, 125, 128, 465, 442, 26, 109, 636, 85, 86, + 442, 123, 259, 106, 130, 95, 84, 117, 254, 253, + 95, 559, 516, 91, 714, 639, 354, 122, 357, 358, + 359, 360, 361, 354, 116, 610, 141, 188, 593, 95, + 543, 478, 381, 135, 127, 342, 542, 126, 95, 95, + 95, 664, 132, 526, 190, 95, 448, 100, 95, 496, + 95, 95, 87, 189, 395, 396, 95, 89, 88, 252, + 563, 193, 494, 495, 493, 682, 680, 345, 441, 133, + 261, 194, 215, 630, 441, 26, 684, 251, 439, 438, + 255, 340, 257, 258, 683, 344, 209, 568, 341, 340, + 341, 340, 263, 567, 341, 340, 342, 383, 566, 725, + 388, 666, 623, 116, 342, 444, 342, 250, 343, 526, + 342, 599, 445, 95, 372, 374, 122, 95, 95, 95, + 95, 129, 715, 716, 341, 340, 95, 408, 29, 402, + 95, 139, 373, 95, 677, 676, 397, 47, 492, 380, + 342, 560, 384, 385, 386, 404, 389, 423, 433, 335, + 390, 391, 392, 579, 580, 581, 398, 595, 512, 411, + 403, 413, 407, 471, 139, 117, 117, 117, 117, 634, + 633, 435, 419, 421, 422, 424, 609, 139, 209, 461, + 95, 95, 116, 116, 116, 116, 410, 462, 412, 520, + 139, 196, 139, 341, 340, 116, 352, 362, 363, 355, + 356, 357, 358, 359, 360, 361, 354, 457, 458, 342, + 467, 234, 468, 469, 239, 240, 241, 242, 243, 244, + 721, 245, 246, 247, 248, 249, 235, 236, 237, 238, + 224, 225, 217, 216, 226, 227, 228, 229, 230, 231, + 232, 233, 353, 352, 362, 363, 355, 356, 357, 358, + 359, 360, 361, 354, 482, 484, 485, 720, 631, 47, + 520, 483, 195, 605, 479, 480, 641, 486, 487, 196, + 476, 450, 451, 426, 490, 196, 587, 632, 541, 471, + 553, 25, 452, 453, 454, 25, 353, 352, 362, 363, + 355, 356, 357, 358, 359, 360, 361, 354, 200, 203, + 47, 201, 541, 202, 204, 546, 547, 668, 383, 522, + 33, 513, 472, 433, 29, 569, 393, 188, 29, 29, + 449, 408, 423, 460, 47, 25, 616, 556, 535, 546, + 547, 12, 408, 491, 523, 539, 471, 456, 519, 517, + 41, 537, 471, 26, 418, 203, 400, 401, 521, 47, + 552, 540, 531, 384, 554, 532, 527, 416, 29, 549, + 433, 417, 144, 415, 548, 414, 105, 407, 36, 562, + 536, 719, 26, 718, 551, 200, 203, 557, 201, 463, + 202, 204, 558, 256, 38, 39, 206, 95, 137, 550, + 107, 102, 691, 95, 98, 103, 47, 33, 690, 408, + 638, 426, 260, 528, 529, 530, 131, 476, 119, 35, + 433, 576, 37, 578, 575, 97, 573, 30, 570, 571, + 490, 574, 1, 90, 620, 437, 427, 582, 120, 40, + 436, 679, 624, 443, 572, 519, 517, 564, 446, 561, + 220, 117, 221, 219, 594, 223, 222, 218, 647, 685, + 600, 205, 210, 473, 42, 371, 108, 598, 116, 674, + 383, 538, 394, 186, 689, 637, 613, 597, 387, 524, + 615, 150, 481, 408, 47, 433, 47, 47, 589, 491, + 590, 619, 612, 160, 618, 433, 592, 433, 622, 611, + 95, 157, 159, 601, 602, 603, 604, 158, 617, 147, + 519, 614, 606, 607, 608, 399, 346, 148, 407, 142, + 115, 191, 199, 197, 47, 375, 376, 377, 378, 379, + 640, 645, 635, 665, 544, 433, 114, 470, 591, 709, + 47, 47, 535, 667, 14, 34, 596, 47, 110, 47, + 171, 671, 672, 11, 554, 663, 669, 10, 675, 17, + 433, 646, 9, 8, 7, 6, 47, 644, 673, 5, + 47, 4, 104, 93, 93, 13, 2, 433, 408, 0, + 0, 433, 0, 0, 536, 0, 0, 670, 0, 93, + 0, 698, 0, 0, 0, 25, 27, 15, 16, 0, + 711, 712, 0, 687, 0, 535, 0, 646, 47, 717, + 0, 0, 22, 688, 686, 0, 0, 0, 713, 622, + 0, 694, 0, 695, 722, 383, 723, 0, 29, 0, + 0, 0, 0, 0, 0, 93, 0, 0, 0, 0, + 93, 0, 0, 0, 0, 47, 47, 536, 0, 26, + 0, 0, 0, 0, 0, 0, 727, 728, 0, 93, + 355, 356, 357, 358, 359, 360, 361, 354, 93, 213, + 93, 0, 0, 0, 0, 93, 0, 0, 93, 678, + 93, 93, 18, 19, 20, 724, 262, 339, 0, 726, + 0, 0, 0, 0, 0, 662, 0, 21, 23, 0, + 0, 24, 0, 0, 0, 696, 0, 697, 0, 699, + 0, 701, 0, 703, 704, 705, 706, 707, 708, 488, + 0, 0, 497, 498, 499, 500, 501, 502, 503, 504, + 505, 506, 507, 508, 509, 510, 511, 0, 28, 0, + 0, 0, 0, 93, 0, 0, 409, 93, 93, 93, + 93, 0, 0, 0, 0, 0, 420, 0, 0, 0, + 93, 649, 0, 213, 658, 659, 660, 657, 0, 661, + 0, 0, 0, 0, 651, 0, 654, 656, 650, 0, + 648, 0, 0, 652, 0, 0, 0, 68, 0, 515, + 0, 146, 0, 653, 655, 0, 145, 0, 177, 60, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 29, + 93, 93, 184, 162, 161, 163, 164, 165, 166, 588, + 0, 167, 168, 169, 0, 0, 0, 0, 143, 155, + 0, 176, 0, 0, 0, 0, 0, 0, 0, 353, + 352, 362, 363, 355, 356, 357, 358, 359, 360, 361, + 354, 152, 153, 337, 0, 0, 0, 182, 0, 154, + 0, 156, 0, 0, 0, 0, 0, 583, 584, 585, + 0, 0, 181, 0, 69, 0, 0, 57, 0, 0, + 52, 0, 0, 77, 72, 64, 58, 59, 48, 0, + 70, 55, 56, 54, 67, 74, 75, 53, 82, 51, + 81, 50, 66, 73, 78, 65, 62, 49, 76, 63, + 61, 0, 71, 79, 83, 0, 178, 179, 180, 183, + 174, 175, 0, 80, 0, 0, 0, 0, 0, 0, + 0, 339, 262, 0, 68, 0, 0, 0, 0, 0, + 409, 339, 339, 339, 0, 0, 60, 0, 0, 0, + 0, 409, 0, 0, 348, 0, 351, 0, 0, 46, + 642, 643, 364, 365, 366, 367, 368, 369, 0, 349, + 350, 347, 370, 353, 352, 362, 363, 355, 356, 357, + 358, 359, 360, 361, 354, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 353, 352, 362, 363, 355, 356, + 357, 358, 359, 360, 361, 354, 0, 0, 0, 0, + 0, 0, 0, 0, 693, 0, 0, 93, 409, 0, + 0, 69, 339, 93, 57, 0, 0, 52, 0, 262, + 77, 72, 64, 58, 59, 48, 0, 70, 55, 56, + 54, 67, 74, 75, 53, 82, 51, 81, 50, 66, + 73, 78, 65, 62, 49, 76, 63, 61, 0, 71, + 79, 83, 0, 0, 0, 0, 339, 0, 0, 0, + 80, 0, 0, 0, 339, 353, 352, 362, 363, 355, + 356, 357, 358, 359, 360, 361, 354, 0, 0, 0, + 0, 0, 409, 0, 262, 362, 363, 355, 356, 357, + 358, 359, 360, 361, 354, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 93, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 339, 327, 318, 297, 329, 278, 289, + 333, 290, 291, 312, 270, 305, 68, 0, 281, 266, + 287, 267, 279, 299, 302, 277, 320, 331, 60, 301, + 303, 317, 296, 313, 273, 307, 310, 330, 0, 0, + 0, 46, 0, 431, 432, 0, 0, 409, 0, 0, + 309, 326, 288, 0, 0, 311, 265, 308, 0, 269, + 271, 332, 324, 284, 285, 555, 0, 0, 0, 0, + 0, 300, 304, 314, 294, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 282, 0, 306, 0, 0, 0, + 298, 0, 272, 0, 283, 315, 264, 322, 295, 325, + 293, 292, 328, 69, 0, 0, 57, 321, 280, 52, + 286, 334, 77, 72, 64, 58, 59, 48, 0, 70, + 55, 56, 54, 67, 74, 75, 53, 82, 51, 81, + 50, 66, 73, 78, 65, 62, 49, 76, 63, 61, + 268, 71, 79, 83, 323, 276, 274, 275, 316, 0, + 0, 319, 80, 327, 318, 297, 329, 278, 289, 333, + 290, 291, 312, 270, 305, 68, 0, 281, 266, 287, + 267, 279, 299, 302, 277, 320, 331, 60, 301, 303, + 317, 296, 313, 273, 307, 310, 330, 0, 0, 0, + 46, 0, 431, 432, 0, 0, 0, 0, 0, 309, + 326, 288, 0, 0, 311, 265, 308, 0, 269, 271, + 332, 324, 284, 285, 0, 0, 0, 0, 0, 0, + 300, 304, 314, 294, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 282, 0, 306, 0, 0, 0, 298, + 0, 272, 0, 283, 315, 264, 322, 295, 325, 293, + 292, 328, 69, 0, 0, 57, 321, 280, 52, 286, + 334, 77, 72, 64, 58, 59, 48, 0, 70, 55, + 56, 54, 67, 74, 75, 53, 82, 51, 81, 50, + 66, 73, 78, 65, 62, 49, 76, 63, 61, 268, + 71, 79, 83, 323, 276, 274, 275, 316, 0, 0, + 319, 80, 327, 318, 297, 329, 278, 289, 333, 290, + 291, 312, 270, 305, 68, 0, 281, 266, 287, 267, + 279, 299, 302, 277, 320, 331, 60, 301, 303, 317, + 296, 313, 273, 307, 310, 330, 0, 0, 0, 46, + 0, 0, 0, 0, 0, 0, 0, 0, 309, 326, + 288, 0, 0, 311, 265, 308, 0, 269, 271, 332, + 324, 284, 285, 0, 0, 0, 0, 0, 0, 300, + 304, 314, 294, 0, 0, 0, 0, 0, 0, 0, + 692, 0, 282, 0, 306, 0, 0, 0, 298, 0, + 272, 0, 283, 315, 264, 322, 295, 325, 293, 292, + 328, 69, 0, 0, 57, 321, 280, 52, 286, 334, + 77, 72, 64, 58, 59, 48, 0, 70, 55, 56, + 54, 67, 74, 75, 53, 82, 51, 81, 50, 66, + 73, 78, 65, 62, 49, 76, 63, 61, 268, 71, + 79, 83, 323, 276, 274, 275, 316, 0, 0, 319, + 80, 327, 318, 297, 329, 278, 289, 333, 290, 291, + 312, 270, 305, 68, 0, 281, 266, 287, 267, 279, + 299, 302, 277, 320, 331, 60, 301, 303, 317, 296, + 313, 273, 307, 310, 330, 29, 0, 0, 46, 0, + 0, 0, 0, 0, 0, 0, 0, 309, 326, 288, + 0, 0, 311, 265, 308, 0, 269, 271, 332, 324, + 284, 285, 0, 0, 0, 0, 0, 0, 300, 304, + 314, 294, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 282, 0, 306, 0, 0, 0, 298, 0, 272, + 0, 283, 315, 264, 322, 295, 325, 293, 292, 328, + 69, 0, 0, 57, 321, 280, 52, 286, 334, 77, 72, 64, 58, 59, 48, 0, 70, 55, 56, 54, 67, 74, 75, 53, 82, 51, 81, 50, 66, 73, - 78, 65, 62, 49, 76, 63, 61, 0, 71, 79, - 83, 0, 179, 180, 181, 184, 174, 175, 176, 0, - 80, 0, 0, 0, 0, 341, 263, 0, 0, 68, - 0, 412, 0, 0, 0, 412, 341, 341, 341, 0, - 0, 60, 0, 0, 0, 235, 412, 0, 240, 241, - 242, 243, 244, 245, 46, 246, 247, 248, 249, 250, - 236, 237, 238, 239, 225, 226, 0, 0, 227, 228, - 229, 230, 231, 232, 233, 234, 364, 365, 357, 358, - 359, 360, 361, 362, 363, 356, 0, 0, 0, 355, - 354, 364, 365, 357, 358, 359, 360, 361, 362, 363, - 356, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 93, 412, 0, 0, 69, 341, 93, 57, - 0, 0, 52, 0, 263, 77, 72, 64, 58, 59, + 78, 65, 62, 49, 76, 63, 61, 268, 71, 79, + 83, 323, 276, 274, 275, 316, 0, 0, 319, 80, + 327, 318, 297, 329, 278, 289, 333, 290, 291, 312, + 270, 305, 68, 0, 281, 266, 287, 267, 279, 299, + 302, 277, 320, 331, 60, 301, 303, 317, 296, 313, + 273, 307, 310, 330, 0, 0, 0, 184, 0, 0, + 0, 0, 0, 0, 0, 0, 309, 326, 288, 0, + 0, 311, 265, 308, 0, 269, 271, 332, 324, 284, + 285, 0, 0, 0, 0, 0, 0, 300, 304, 314, + 294, 0, 0, 0, 0, 0, 0, 0, 577, 0, + 282, 0, 306, 0, 0, 0, 298, 0, 272, 0, + 283, 315, 264, 322, 295, 325, 293, 292, 328, 69, + 0, 0, 57, 321, 280, 52, 286, 334, 77, 72, + 64, 58, 59, 48, 0, 70, 55, 56, 54, 67, + 74, 75, 53, 82, 51, 81, 50, 66, 73, 78, + 65, 62, 49, 76, 63, 61, 268, 71, 79, 83, + 323, 276, 274, 275, 316, 0, 0, 319, 80, 327, + 318, 297, 329, 278, 289, 333, 290, 291, 312, 270, + 305, 68, 0, 281, 266, 287, 267, 279, 299, 302, + 277, 320, 331, 60, 301, 303, 317, 296, 313, 273, + 307, 310, 330, 0, 0, 0, 46, 0, 0, 0, + 0, 0, 0, 0, 0, 309, 326, 288, 0, 0, + 311, 265, 308, 0, 269, 271, 332, 324, 284, 285, + 0, 0, 0, 0, 0, 0, 300, 304, 314, 294, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 282, + 0, 306, 0, 0, 0, 298, 0, 272, 0, 283, + 315, 264, 322, 295, 325, 293, 292, 328, 69, 0, + 0, 57, 321, 280, 52, 286, 334, 77, 72, 64, + 58, 59, 48, 0, 70, 55, 56, 54, 67, 74, + 75, 53, 82, 51, 81, 50, 66, 73, 78, 65, + 62, 49, 76, 63, 61, 268, 71, 79, 83, 323, + 276, 274, 275, 316, 0, 0, 319, 80, 327, 318, + 297, 329, 278, 289, 333, 290, 291, 312, 270, 305, + 68, 0, 281, 266, 287, 267, 279, 299, 302, 277, + 320, 331, 60, 301, 303, 317, 296, 313, 273, 307, + 310, 330, 0, 0, 0, 184, 0, 0, 0, 0, + 0, 0, 0, 0, 309, 326, 288, 0, 0, 311, + 265, 308, 0, 269, 271, 332, 324, 284, 285, 0, + 0, 0, 0, 0, 0, 300, 304, 314, 294, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 282, 0, + 306, 0, 0, 0, 298, 0, 272, 0, 283, 315, + 264, 322, 295, 325, 293, 292, 328, 69, 0, 0, + 57, 321, 280, 52, 286, 334, 77, 72, 64, 58, + 59, 48, 0, 70, 55, 56, 54, 67, 74, 75, + 53, 82, 51, 81, 50, 66, 73, 78, 65, 62, + 49, 76, 63, 61, 268, 71, 79, 83, 323, 276, + 274, 275, 316, 0, 0, 319, 80, 327, 318, 297, + 329, 278, 289, 333, 290, 291, 312, 270, 305, 68, + 0, 281, 266, 287, 267, 279, 299, 302, 277, 320, + 331, 60, 301, 303, 317, 296, 313, 273, 307, 310, + 330, 0, 0, 0, 94, 0, 0, 0, 0, 0, + 0, 0, 0, 309, 326, 288, 0, 0, 311, 265, + 308, 0, 269, 271, 332, 324, 284, 285, 0, 0, + 0, 0, 0, 0, 300, 304, 314, 294, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 282, 0, 306, + 0, 0, 0, 298, 0, 272, 0, 283, 315, 264, + 322, 295, 325, 293, 292, 328, 69, 0, 0, 57, + 321, 280, 52, 286, 334, 77, 72, 64, 58, 59, 48, 0, 70, 55, 56, 54, 67, 74, 75, 53, 82, 51, 81, 50, 66, 73, 78, 65, 62, 49, - 76, 63, 61, 0, 71, 79, 83, 0, 0, 0, - 0, 341, 0, 0, 0, 0, 80, 575, 0, 0, - 341, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 412, 0, - 263, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 93, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 341, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 76, 63, 61, 268, 71, 79, 83, 323, 276, 274, + 275, 316, 68, 0, 319, 80, 146, 0, 0, 0, + 0, 145, 0, 177, 60, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 29, 0, 0, 184, 162, 161, + 163, 164, 165, 166, 0, 0, 167, 168, 169, 0, + 0, 0, 0, 143, 155, 0, 176, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 152, 153, 337, 0, + 0, 0, 182, 0, 154, 0, 156, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 181, 0, 69, + 0, 0, 57, 0, 0, 52, 0, 0, 77, 72, + 64, 58, 59, 48, 0, 70, 55, 56, 54, 67, + 74, 75, 53, 82, 51, 81, 50, 66, 73, 78, + 65, 62, 49, 76, 63, 61, 0, 71, 79, 83, + 0, 178, 179, 180, 183, 174, 175, 68, 80, 0, + 0, 146, 0, 0, 0, 0, 145, 0, 177, 60, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 29, + 0, 139, 184, 162, 161, 163, 164, 165, 166, 0, + 0, 167, 168, 169, 0, 0, 0, 0, 143, 155, + 0, 176, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 640, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 412, 329, 320, 299, 331, 280, - 291, 335, 292, 293, 314, 271, 307, 68, 0, 283, - 267, 289, 268, 281, 301, 304, 279, 322, 333, 60, - 303, 305, 319, 298, 315, 274, 309, 312, 332, 0, - 0, 0, 46, 0, 434, 435, 0, 0, 0, 0, - 0, 311, 328, 290, 0, 0, 313, 266, 310, 0, - 270, 272, 334, 326, 286, 287, 559, 0, 0, 0, - 0, 0, 302, 306, 316, 296, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 284, 0, 308, 0, 0, - 0, 300, 0, 273, 0, 285, 317, 265, 324, 297, - 327, 295, 294, 330, 69, 0, 0, 57, 323, 282, - 52, 288, 336, 77, 72, 64, 58, 59, 48, 0, - 70, 55, 56, 54, 67, 74, 75, 53, 82, 51, - 81, 50, 66, 73, 78, 65, 62, 49, 76, 63, - 61, 269, 71, 79, 83, 325, 277, 275, 276, 318, - 0, 278, 0, 321, 80, 329, 320, 299, 331, 280, - 291, 335, 292, 293, 314, 271, 307, 68, 0, 283, - 267, 289, 268, 281, 301, 304, 279, 322, 333, 60, - 303, 305, 319, 298, 315, 274, 309, 312, 332, 0, - 0, 0, 46, 0, 434, 435, 0, 0, 0, 0, - 0, 311, 328, 290, 0, 0, 313, 266, 310, 0, - 270, 272, 334, 326, 286, 287, 0, 0, 0, 0, - 0, 0, 302, 306, 316, 296, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 284, 0, 308, 0, 0, - 0, 300, 0, 273, 0, 285, 317, 265, 324, 297, - 327, 295, 294, 330, 69, 0, 0, 57, 323, 282, - 52, 288, 336, 77, 72, 64, 58, 59, 48, 0, - 70, 55, 56, 54, 67, 74, 75, 53, 82, 51, - 81, 50, 66, 73, 78, 65, 62, 49, 76, 63, - 61, 269, 71, 79, 83, 325, 277, 275, 276, 318, - 0, 278, 0, 321, 80, 329, 320, 299, 331, 280, - 291, 335, 292, 293, 314, 271, 307, 68, 0, 283, - 267, 289, 268, 281, 301, 304, 279, 322, 333, 60, - 303, 305, 319, 298, 315, 274, 309, 312, 332, 0, - 0, 0, 46, 0, 0, 0, 0, 0, 0, 0, - 0, 311, 328, 290, 0, 0, 313, 266, 310, 0, - 270, 272, 334, 326, 286, 287, 0, 0, 0, 0, - 0, 0, 302, 306, 316, 296, 0, 0, 0, 0, - 0, 0, 0, 698, 0, 284, 0, 308, 0, 0, - 0, 300, 0, 273, 0, 285, 317, 265, 324, 297, - 327, 295, 294, 330, 69, 0, 0, 57, 323, 282, - 52, 288, 336, 77, 72, 64, 58, 59, 48, 0, - 70, 55, 56, 54, 67, 74, 75, 53, 82, 51, - 81, 50, 66, 73, 78, 65, 62, 49, 76, 63, - 61, 269, 71, 79, 83, 325, 277, 275, 276, 318, - 0, 278, 0, 321, 80, 329, 320, 299, 331, 280, - 291, 335, 292, 293, 314, 271, 307, 68, 0, 283, - 267, 289, 268, 281, 301, 304, 279, 322, 333, 60, - 303, 305, 319, 298, 315, 274, 309, 312, 332, 29, - 0, 0, 46, 0, 0, 0, 0, 0, 0, 0, - 0, 311, 328, 290, 0, 0, 313, 266, 310, 0, - 270, 272, 334, 326, 286, 287, 0, 0, 0, 0, - 0, 0, 302, 306, 316, 296, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 284, 0, 308, 0, 0, - 0, 300, 0, 273, 0, 285, 317, 265, 324, 297, - 327, 295, 294, 330, 69, 0, 0, 57, 323, 282, - 52, 288, 336, 77, 72, 64, 58, 59, 48, 0, - 70, 55, 56, 54, 67, 74, 75, 53, 82, 51, - 81, 50, 66, 73, 78, 65, 62, 49, 76, 63, - 61, 269, 71, 79, 83, 325, 277, 275, 276, 318, - 0, 278, 0, 321, 80, 329, 320, 299, 331, 280, - 291, 335, 292, 293, 314, 271, 307, 68, 0, 283, - 267, 289, 268, 281, 301, 304, 279, 322, 333, 60, - 303, 305, 319, 298, 315, 274, 309, 312, 332, 0, - 0, 0, 185, 0, 0, 0, 0, 0, 0, 0, - 0, 311, 328, 290, 0, 0, 313, 266, 310, 0, - 270, 272, 334, 326, 286, 287, 0, 0, 0, 0, - 0, 0, 302, 306, 316, 296, 0, 0, 0, 0, - 0, 0, 0, 581, 0, 284, 0, 308, 0, 0, - 0, 300, 0, 273, 0, 285, 317, 265, 324, 297, - 327, 295, 294, 330, 69, 0, 0, 57, 323, 282, - 52, 288, 336, 77, 72, 64, 58, 59, 48, 0, - 70, 55, 56, 54, 67, 74, 75, 53, 82, 51, - 81, 50, 66, 73, 78, 65, 62, 49, 76, 63, - 61, 269, 71, 79, 83, 325, 277, 275, 276, 318, - 0, 278, 0, 321, 80, 329, 320, 299, 331, 280, - 291, 335, 292, 293, 314, 271, 307, 68, 0, 283, - 267, 289, 268, 281, 301, 304, 279, 322, 333, 60, - 303, 305, 319, 298, 315, 274, 309, 312, 332, 0, - 0, 0, 46, 0, 0, 0, 0, 0, 0, 0, - 0, 311, 328, 290, 0, 0, 313, 266, 310, 0, - 270, 272, 334, 326, 286, 287, 0, 0, 0, 0, - 0, 0, 302, 306, 316, 296, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 284, 0, 308, 0, 0, - 0, 300, 0, 273, 0, 285, 317, 265, 324, 297, - 327, 295, 294, 330, 69, 0, 0, 57, 323, 282, - 52, 288, 336, 77, 72, 64, 58, 59, 48, 0, - 70, 55, 56, 54, 67, 74, 75, 53, 82, 51, - 81, 50, 66, 73, 78, 65, 62, 49, 76, 63, - 61, 269, 71, 79, 83, 325, 277, 275, 276, 318, - 0, 278, 0, 321, 80, 329, 320, 299, 331, 280, - 291, 335, 292, 293, 314, 271, 307, 68, 0, 283, - 267, 289, 268, 281, 301, 304, 279, 322, 333, 60, - 303, 305, 319, 298, 315, 274, 309, 312, 332, 0, - 0, 0, 185, 0, 0, 0, 0, 0, 0, 0, - 0, 311, 328, 290, 0, 0, 313, 266, 310, 0, - 270, 272, 334, 326, 286, 287, 0, 0, 0, 0, - 0, 0, 302, 306, 316, 296, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 284, 0, 308, 0, 0, - 0, 300, 0, 273, 0, 285, 317, 265, 324, 297, - 327, 295, 294, 330, 69, 0, 0, 57, 323, 282, - 52, 288, 336, 77, 72, 64, 58, 59, 48, 0, - 70, 55, 56, 54, 67, 74, 75, 53, 82, 51, - 81, 50, 66, 73, 78, 65, 62, 49, 76, 63, - 61, 269, 71, 79, 83, 325, 277, 275, 276, 318, - 0, 278, 0, 321, 80, 329, 320, 299, 331, 280, - 291, 335, 292, 293, 314, 271, 307, 68, 0, 283, - 267, 289, 268, 281, 301, 304, 279, 322, 333, 60, - 303, 305, 319, 298, 315, 274, 309, 312, 332, 0, - 0, 0, 94, 0, 0, 0, 0, 0, 0, 0, - 0, 311, 328, 290, 0, 0, 313, 266, 310, 0, - 270, 272, 334, 326, 286, 287, 0, 0, 0, 0, - 0, 0, 302, 306, 316, 296, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 284, 0, 308, 0, 0, - 0, 300, 0, 273, 0, 285, 317, 265, 324, 297, - 327, 295, 294, 330, 69, 0, 0, 57, 323, 282, - 52, 288, 336, 77, 72, 64, 58, 59, 48, 0, + 0, 152, 153, 0, 0, 0, 0, 182, 0, 154, + 0, 156, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 181, 0, 69, 0, 0, 57, 0, 0, + 52, 0, 0, 77, 72, 64, 58, 59, 48, 0, 70, 55, 56, 54, 67, 74, 75, 53, 82, 51, 81, 50, 66, 73, 78, 65, 62, 49, 76, 63, - 61, 269, 71, 79, 83, 325, 277, 275, 276, 318, - 0, 278, 68, 321, 80, 0, 146, 0, 0, 0, - 0, 145, 0, 178, 60, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 29, 0, 0, 185, 162, 161, + 61, 25, 71, 79, 83, 0, 178, 179, 180, 183, + 174, 175, 68, 80, 0, 0, 146, 0, 0, 0, + 0, 145, 0, 177, 60, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 29, 0, 0, 184, 162, 161, 163, 164, 165, 166, 0, 0, 167, 168, 169, 0, - 0, 0, 0, 143, 155, 0, 177, 0, 0, 0, + 0, 0, 0, 143, 155, 0, 176, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 152, 153, 339, 0, - 0, 0, 183, 0, 154, 0, 156, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 182, 0, 69, + 0, 0, 0, 0, 0, 0, 152, 153, 0, 0, + 0, 0, 182, 0, 154, 0, 156, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 181, 0, 69, 0, 0, 57, 0, 0, 52, 0, 0, 77, 72, 64, 58, 59, 48, 0, 70, 55, 56, 54, 67, 74, 75, 53, 82, 51, 81, 50, 66, 73, 78, 65, 62, 49, 76, 63, 61, 0, 71, 79, 83, - 0, 179, 180, 181, 184, 174, 175, 176, 68, 80, - 0, 0, 146, 0, 0, 0, 0, 145, 0, 178, - 60, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 29, 0, 139, 185, 162, 161, 163, 164, 165, 166, - 0, 0, 167, 168, 169, 0, 0, 0, 0, 143, - 155, 0, 177, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 152, 153, 0, 0, 0, 0, 183, 0, - 154, 0, 156, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 182, 0, 69, 0, 0, 57, 0, - 0, 52, 0, 0, 77, 72, 64, 58, 59, 48, - 0, 70, 55, 56, 54, 67, 74, 75, 53, 82, - 51, 81, 50, 66, 73, 78, 65, 62, 49, 76, - 63, 61, 0, 71, 79, 83, 25, 179, 180, 181, - 184, 174, 175, 176, 0, 80, 0, 68, 0, 0, - 0, 146, 0, 0, 0, 0, 145, 0, 178, 60, + 0, 178, 179, 180, 183, 174, 175, 68, 80, 0, + 0, 146, 0, 0, 0, 0, 145, 0, 177, 60, 0, 0, 0, 0, 0, 0, 0, 0, 0, 29, - 0, 0, 185, 162, 161, 163, 164, 165, 166, 0, + 0, 0, 184, 162, 161, 163, 164, 165, 166, 0, 0, 167, 168, 169, 0, 0, 0, 0, 143, 155, - 0, 177, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 176, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 152, 153, 0, 0, 0, 0, 183, 0, 154, + 0, 152, 153, 0, 0, 0, 0, 182, 0, 154, 0, 156, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 182, 0, 69, 0, 0, 57, 0, 0, + 0, 0, 181, 0, 69, 0, 0, 57, 0, 0, 52, 0, 0, 77, 72, 64, 58, 59, 48, 0, 70, 55, 56, 54, 67, 74, 75, 53, 82, 51, 81, 50, 66, 73, 78, 65, 62, 49, 76, 63, - 61, 0, 71, 79, 83, 0, 179, 180, 181, 184, - 174, 175, 176, 68, 80, 0, 0, 146, 0, 0, - 0, 0, 145, 0, 178, 60, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 29, 0, 0, 185, 162, - 161, 163, 164, 165, 166, 0, 0, 167, 168, 169, - 0, 0, 0, 0, 143, 155, 0, 177, 0, 0, + 61, 0, 71, 79, 83, 68, 178, 179, 180, 183, + 174, 175, 0, 80, 0, 0, 177, 60, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 29, 0, 0, + 184, 162, 161, 163, 164, 165, 166, 0, 0, 167, + 168, 169, 0, 0, 0, 0, 0, 155, 0, 176, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 152, 153, 0, - 0, 0, 0, 183, 0, 154, 0, 156, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 182, 0, - 69, 0, 0, 57, 0, 0, 52, 0, 0, 77, - 72, 64, 58, 59, 48, 0, 70, 55, 56, 54, - 67, 74, 75, 53, 82, 51, 81, 50, 66, 73, - 78, 65, 62, 49, 76, 63, 61, 0, 71, 79, - 83, 68, 179, 180, 181, 184, 174, 175, 176, 0, - 80, 0, 178, 60, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 29, 0, 0, 185, 162, 161, 163, - 164, 165, 166, 0, 0, 167, 168, 169, 0, 0, - 0, 0, 0, 155, 0, 177, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 152, + 153, 0, 0, 0, 0, 182, 0, 154, 0, 156, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 152, 153, 0, 0, 0, - 0, 183, 0, 154, 0, 156, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 182, 0, 69, 0, - 0, 57, 0, 0, 52, 0, 0, 77, 72, 64, + 181, 0, 69, 0, 0, 57, 0, 0, 52, 0, + 0, 77, 72, 64, 58, 59, 48, 0, 70, 55, + 56, 54, 67, 74, 75, 53, 82, 51, 81, 50, + 66, 73, 78, 65, 62, 49, 76, 63, 61, 0, + 71, 79, 83, 0, 178, 179, 180, 183, 174, 175, + 68, 80, 0, 475, 0, 0, 0, 0, 0, 0, + 0, 0, 60, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 46, 0, 477, 0, 0, + 25, 0, 0, 0, 0, 0, 0, 0, 0, 341, + 340, 68, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 60, 0, 342, 0, 0, 0, 0, + 0, 0, 0, 29, 0, 0, 46, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 69, 0, 0, + 57, 0, 0, 52, 0, 0, 77, 72, 64, 58, + 59, 48, 0, 70, 55, 56, 54, 67, 74, 75, + 53, 82, 51, 81, 50, 66, 73, 78, 65, 62, + 49, 76, 63, 61, 0, 71, 79, 83, 69, 0, + 0, 57, 0, 0, 52, 0, 80, 77, 72, 64, 58, 59, 48, 0, 70, 55, 56, 54, 67, 74, 75, 53, 82, 51, 81, 50, 66, 73, 78, 65, - 62, 49, 76, 63, 61, 0, 71, 79, 83, 0, - 179, 180, 181, 184, 174, 175, 176, 68, 80, 0, - 478, 0, 0, 0, 0, 0, 0, 0, 0, 60, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 46, 0, 480, 0, 0, 0, 25, 0, - 0, 0, 0, 0, 0, 0, 343, 342, 0, 68, + 62, 49, 76, 63, 61, 25, 71, 79, 83, 0, + 0, 0, 0, 0, 0, 0, 68, 80, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 60, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 29, 0, + 0, 94, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 68, 0, 0, + 212, 0, 0, 0, 0, 0, 0, 0, 0, 60, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 60, 344, 0, 0, 0, 0, 0, 0, 0, - 0, 29, 0, 0, 46, 0, 0, 0, 0, 0, + 0, 0, 94, 0, 214, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 69, 0, 0, 57, 0, 0, - 52, 0, 0, 77, 72, 64, 58, 59, 48, 0, + 0, 0, 0, 69, 0, 0, 57, 0, 0, 52, + 0, 0, 77, 72, 64, 58, 59, 48, 0, 70, + 55, 56, 54, 67, 74, 75, 53, 82, 51, 81, + 50, 66, 73, 78, 65, 62, 49, 76, 63, 61, + 0, 71, 79, 83, 69, 0, 0, 57, 0, 0, + 52, 0, 80, 77, 72, 64, 58, 59, 48, 0, 70, 55, 56, 54, 67, 74, 75, 53, 82, 51, 81, 50, 66, 73, 78, 65, 62, 49, 76, 63, - 61, 0, 71, 79, 83, 0, 69, 0, 0, 57, - 0, 0, 52, 0, 80, 77, 72, 64, 58, 59, - 48, 0, 70, 55, 56, 54, 67, 74, 75, 53, - 82, 51, 81, 50, 66, 73, 78, 65, 62, 49, - 76, 63, 61, 25, 71, 79, 83, 0, 0, 0, - 0, 0, 0, 0, 68, 0, 80, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 60, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 29, 0, 0, 94, + 61, 0, 71, 79, 83, 68, 0, 0, 0, 0, + 0, 0, 0, 80, 0, 0, 0, 60, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 68, 0, 0, 213, + 46, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 68, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 60, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 94, 0, 215, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 69, 0, 0, 57, 0, 0, 52, 0, 0, - 77, 72, 64, 58, 59, 48, 0, 70, 55, 56, - 54, 67, 74, 75, 53, 82, 51, 81, 50, 66, - 73, 78, 65, 62, 49, 76, 63, 61, 0, 71, - 79, 83, 0, 69, 0, 0, 57, 0, 0, 52, + 0, 46, 0, 477, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 43, 0, 0, 0, + 0, 44, 69, 0, 0, 57, 0, 0, 52, 0, + 0, 77, 72, 64, 58, 59, 48, 0, 70, 55, + 56, 54, 67, 74, 75, 53, 82, 51, 81, 50, + 66, 73, 78, 65, 62, 49, 76, 63, 61, 0, + 71, 79, 83, 69, 0, 0, 57, 0, 0, 52, 0, 80, 77, 72, 64, 58, 59, 48, 0, 70, 55, 56, 54, 67, 74, 75, 53, 82, 51, 81, 50, 66, 73, 78, 65, 62, 49, 76, 63, 61, - 68, 71, 79, 83, 0, 0, 0, 0, 0, 0, - 0, 0, 60, 80, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 46, 0, 0, 0, 0, + 0, 71, 79, 83, 68, 0, 0, 0, 0, 0, + 0, 0, 80, 0, 0, 0, 60, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 29, 0, 0, 94, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 68, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 60, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 46, 0, 480, + 0, 0, 0, 0, 0, 68, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 60, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 43, 0, 0, 0, 0, 44, 69, 0, 0, - 57, 0, 0, 52, 0, 0, 77, 72, 64, 58, - 59, 48, 0, 70, 55, 56, 54, 67, 74, 75, - 53, 82, 51, 81, 50, 66, 73, 78, 65, 62, - 49, 76, 63, 61, 0, 71, 79, 83, 0, 69, - 0, 0, 57, 0, 0, 52, 0, 80, 77, 72, - 64, 58, 59, 48, 0, 70, 55, 56, 54, 67, - 74, 75, 53, 82, 51, 81, 50, 66, 73, 78, - 65, 62, 49, 76, 63, 61, 68, 71, 79, 83, - 0, 0, 0, 0, 0, 0, 0, 0, 60, 80, - 0, 0, 0, 0, 0, 0, 0, 0, 29, 0, - 0, 94, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 68, 0, + 94, 0, 214, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 60, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 94, 0, 215, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 69, 0, 0, 57, 0, 0, 52, - 0, 0, 77, 72, 64, 58, 59, 48, 0, 70, - 55, 56, 54, 67, 74, 75, 53, 82, 51, 81, - 50, 66, 73, 78, 65, 62, 49, 76, 63, 61, - 0, 71, 79, 83, 0, 69, 0, 0, 57, 0, - 0, 52, 0, 80, 77, 72, 64, 58, 59, 48, - 0, 70, 55, 56, 54, 67, 74, 75, 53, 82, - 51, 81, 50, 66, 73, 78, 65, 62, 49, 76, - 63, 61, 68, 71, 79, 83, 0, 0, 0, 193, - 0, 0, 0, 0, 60, 80, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 94, 0, 0, + 0, 69, 0, 0, 57, 0, 0, 52, 0, 0, + 77, 72, 64, 58, 59, 48, 0, 70, 55, 56, + 54, 67, 74, 75, 53, 82, 51, 81, 50, 66, + 73, 78, 65, 62, 49, 76, 63, 61, 0, 71, + 79, 83, 69, 0, 0, 57, 0, 0, 52, 0, + 80, 77, 72, 64, 58, 59, 48, 0, 70, 55, + 56, 54, 67, 74, 75, 53, 82, 51, 81, 50, + 66, 73, 78, 65, 62, 49, 76, 63, 61, 0, + 71, 79, 83, 68, 0, 0, 0, 0, 0, 0, + 192, 80, 0, 0, 0, 60, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 94, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 68, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 60, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 46, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 69, 0, 0, 57, 0, 0, 52, 0, 0, 77, + 72, 64, 58, 59, 48, 0, 70, 55, 56, 54, + 67, 74, 75, 53, 82, 51, 81, 50, 66, 73, + 78, 65, 62, 49, 76, 63, 61, 0, 71, 79, + 83, 69, 0, 0, 57, 0, 0, 52, 0, 80, + 77, 72, 64, 58, 59, 48, 0, 70, 55, 56, + 54, 67, 74, 75, 53, 82, 51, 81, 50, 66, + 73, 78, 65, 62, 49, 76, 63, 61, 0, 71, + 79, 83, 68, 0, 0, 0, 0, 0, 0, 0, + 80, 0, 0, 0, 60, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 184, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 68, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 60, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 94, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 69, 0, 0, 57, 0, 0, 52, 0, 0, 77, 72, 64, 58, 59, 48, 0, 70, 55, 56, 54, 67, 74, 75, 53, 82, 51, 81, 50, 66, 73, 78, 65, 62, 49, 76, 63, 61, 0, 71, 79, 83, - 0, 69, 0, 0, 57, 0, 0, 52, 0, 80, - 77, 72, 64, 58, 59, 48, 0, 70, 55, 56, - 54, 67, 74, 75, 53, 82, 51, 81, 50, 66, - 73, 78, 65, 62, 49, 76, 63, 61, 68, 71, - 79, 83, 0, 0, 0, 0, 0, 0, 0, 0, - 60, 80, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 185, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 68, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 60, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 94, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 69, 0, 0, 57, 0, - 0, 52, 0, 0, 77, 72, 64, 58, 59, 48, - 0, 70, 55, 56, 54, 67, 74, 75, 53, 82, - 51, 81, 50, 66, 73, 78, 65, 62, 49, 76, - 63, 61, 0, 71, 79, 83, 0, 69, 0, 0, - 57, 0, 0, 52, 0, 80, 77, 72, 64, 58, - 59, 48, 0, 70, 55, 56, 54, 67, 74, 75, - 53, 82, 51, 81, 50, 66, 73, 78, 65, 62, - 49, 76, 63, 61, 0, 71, 79, 83, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 80, + 69, 0, 0, 57, 0, 0, 52, 0, 80, 77, + 72, 64, 58, 59, 48, 0, 70, 55, 56, 54, + 67, 74, 75, 53, 82, 51, 81, 50, 66, 73, + 78, 65, 62, 49, 76, 63, 61, 0, 71, 79, + 83, 0, 0, 0, 0, 0, 0, 0, 0, 80, } var yyPact = [...]int{ - 94, -1000, -115, -1000, -1000, -1000, -1000, -1000, -1000, -1000, - -1000, -1000, 488, 535, -1000, -1000, -1000, 389, 3493, 7, - -11, 39, 4033, 4033, -1000, -1000, 379, 108, -1000, -1000, - -1000, -1000, 476, 482, 379, 460, -22, -1000, 3659, 529, - -1000, 218, -8, -21, -1000, -1000, -1000, -1000, -1000, -1000, - -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, + 659, -1000, -111, -1000, -1000, -1000, -1000, -1000, -1000, -1000, + -1000, -1000, 463, 484, -1000, -1000, -1000, 381, 3398, -12, + -20, 39, 3946, 3946, -1000, -1000, 355, 71, -1000, -1000, + -1000, -1000, 455, 460, 355, 452, -25, -1000, 3567, 478, + -1000, 154, -18, -31, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, - -1000, -1000, -1000, -1000, 4033, -18, -18, -3, 513, 4033, - -6, -1000, -1000, 23, -1000, -1000, -1000, -1000, 458, 233, - -102, -1000, 2886, 2886, 488, -1000, 379, -1000, 3825, -1000, - 146, 385, 425, -1000, -1000, -1000, 455, 3327, 3369, 4033, - 284, -1000, 945, -21, 4033, 91, 2, 4033, 453, 4033, - 4033, -4, -1000, -1000, 512, 2340, 2475, -1000, -1000, -1000, - -1000, 169, -1000, 2886, 269, 270, 270, -1000, -1000, -1000, - -1000, -1000, 3014, 3014, 3014, 3014, 3014, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, - 270, 22, -1000, 2750, 270, 270, 270, 2886, 270, -1000, - -1000, -1000, 270, 270, 270, -1000, -1000, 344, -1000, 159, - 476, 233, 375, 4033, -1000, -1000, 3991, 3659, 3659, 3659, - 3659, -1000, 429, 427, 412, 417, 4033, -1000, 277, 233, - 3327, -1000, -1000, 3701, -1000, -1000, 509, 1440, 55, 166, - -89, -1000, -1000, 347, -1000, 347, 347, -1000, -1000, -1000, - -1000, -1000, -1000, -1000, -1000, -1000, 347, 347, 347, -1000, - -1000, -1000, -1000, -1000, -1000, -1000, 371, 371, 371, 348, - 348, 227, -1000, 451, -27, -23, -1000, -1000, -1000, -1000, - 4033, 4033, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, + -1000, -1000, -1000, -1000, 3946, -30, -30, -14, 476, 3946, + 4, -1000, -1000, 21, -1000, -1000, -1000, -1000, 449, 170, + -95, -1000, 2790, 2790, 463, -1000, 355, -1000, 3736, -1000, + 101, 315, 425, -1000, -1000, -1000, 446, 3229, 3270, 3946, + 272, -1000, 181, -31, 3946, 81, -8, 3946, 442, 3946, + 3946, -16, -1000, -1000, 472, 2252, 2385, -1000, -1000, -1000, + -1000, 148, -1000, 2790, 966, 360, 360, -1000, -1000, -1000, + -1000, -1000, 2918, 2918, 2918, 2918, 2918, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, + 360, 20, -1000, 2655, 360, 360, 2790, 360, -1000, -1000, + -1000, 360, 360, 360, -1000, -1000, 356, -1000, 112, 455, + 170, 399, 3946, -1000, -1000, 3905, 3567, 3567, 3567, 3567, + -1000, 415, 413, 407, 394, 3946, -1000, 231, 170, 3229, + -1000, -1000, 3608, -1000, -1000, 470, 1358, 55, 134, -82, + -1000, -1000, 361, -1000, 361, 361, -1000, -1000, -1000, -1000, + -1000, -1000, -1000, -1000, -1000, 361, 361, 361, -1000, -1000, + -1000, -1000, -1000, -1000, -1000, 378, 378, 378, 364, 364, + 225, -1000, 438, -35, -32, -1000, -1000, -1000, -1000, 3946, + 3946, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, - -1000, -1000, -1000, -1000, -1000, -1000, -1000, 320, -1000, -1000, - 3150, 21, 2886, 2886, 139, 2886, 2886, 52, 3014, 241, - 148, 3014, 3014, 3014, 3014, 3014, 3014, 3014, 3014, 3014, - 3014, 3014, 3014, 3014, 3014, 3014, -1000, -1000, -1000, -1000, - -1000, -1000, -1000, -1000, 224, -1000, 379, -1000, -1000, -1000, - -1000, 1022, 876, 2190, 275, 78, 2750, 2886, 3991, 458, - 67, 78, 3991, 2475, 2475, 2475, 2886, -1000, -1000, -1000, - -1000, -1000, -1000, 3991, 270, -1000, 3192, -1000, 288, -1000, - 60, -1000, 18, 425, 402, 494, -1000, -1000, -1000, -1000, - 407, -1000, -1000, -1000, -1000, -1000, 233, -1000, 488, 2886, - 340, 1290, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, - 370, 446, 64, 221, -1000, -1000, 439, -1000, 127, -91, - -1000, -1000, 199, -1000, -1000, -1000, -1000, -1000, -1000, 193, - -1000, -1000, -1000, 188, -1000, 387, -1000, 218, -1000, 4033, - 3991, -1000, -1000, 509, 2475, 3659, -1000, -1000, 3535, -1000, - -1000, 1890, 52, 83, -1000, -1000, 184, -1000, -1000, 78, - 78, 312, -1000, -1000, -1000, -1000, 241, 3014, 3014, 3014, - -15, 312, 868, 1007, 672, -1000, 59, 59, 28, 28, - 28, 28, 28, 89, 89, 233, -1000, 233, 2475, 339, - 270, 16, -1000, 2886, -1000, 214, 322, 2475, 121, -1000, - 2886, 233, 253, 253, 253, -1000, 294, 263, -1000, -1000, - 2611, 233, 267, 15, 488, 3991, 2886, 2190, -1000, -1000, - 2886, 369, -1000, -1000, -1000, 476, 78, 1440, -1000, 1440, - 3867, -1000, 210, -1000, -1000, -83, 6, -1000, -1000, -1000, - 248, 291, 261, 4033, -1000, -1000, -14, 502, -1000, 286, - -1000, -1000, 12, -1000, -1000, -1000, -1000, -15, 312, 803, - -1000, 3014, 3014, -1000, -1000, 253, 2475, 2040, 78, 697, - 3014, 319, 62, -1000, 2886, 216, -1000, -1000, -1000, -1000, - 270, -1000, -1000, 377, 3867, 3867, 476, -1000, 78, -1000, - 78, 3867, -1000, 1290, -1000, 242, -1000, 347, -1000, 58, - -1000, -1000, -1000, -1000, -1000, -1000, -1000, 187, -1000, 160, - 1740, 3991, 496, 479, 1590, -1000, 3014, 312, 312, -1000, - 233, -1000, 233, 347, -1000, 347, 348, 347, -42, 347, - -43, 347, 347, 347, 347, 347, 347, -1000, 790, -107, - -1000, 78, 2886, -1000, 270, -1000, 379, 0, -1000, -1000, - 217, -1000, -1000, 3867, -1000, -1000, 443, -1000, 442, 240, - 237, -1000, -1000, -1000, -1000, -1000, 2886, 2886, -1000, 312, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, - -1000, -1000, -1000, -1000, -1000, -1000, 3014, 233, 185, 78, - 263, 233, 3867, 3867, -1000, -1000, -1000, -1000, -1000, -1000, - 78, 251, 403, -1000, -1000, -1000, -1000, -1000, -1000, + -1000, -1000, -1000, -1000, -1000, 382, -1000, -1000, 3053, 19, + 2790, 2790, 283, 2790, 2790, 43, 2918, 169, 69, 2918, + 2918, 2918, 2918, 2918, 2918, 2918, 2918, 2918, 2918, 2918, + 2918, 2918, 2918, 2918, -1000, -1000, -1000, -1000, -1000, -1000, + -1000, -1000, 196, -1000, 355, -1000, -1000, -1000, -1000, 987, + 840, 2103, 229, 114, 2655, 2790, 449, 61, 114, 3905, + 2385, 2385, 2385, 2790, -1000, -1000, -1000, -1000, -1000, -1000, + 3905, 360, -1000, 3094, -1000, 342, -1000, 50, -1000, 18, + 425, 372, 348, -1000, -1000, -1000, -1000, 409, -1000, -1000, + -1000, -1000, -1000, 170, -1000, 463, 2790, 320, 1209, -1000, + -1000, -1000, -1000, -1000, -1000, -1000, -1000, 368, 432, 49, + 179, -1000, -1000, 423, -1000, 89, -87, -1000, -1000, 133, + -1000, -1000, -1000, -1000, -1000, -1000, 128, -1000, -1000, -1000, + 122, -1000, 358, -1000, 154, -1000, 3946, 3905, -1000, -1000, + 470, 2385, 3567, -1000, -1000, 3439, -1000, -1000, 1805, 43, + 104, -1000, -1000, 182, -1000, -1000, 114, 114, 1068, -1000, + -1000, -1000, -1000, 169, 2918, 2918, 2918, 245, 1068, 832, + 1086, 198, -1000, 15, 15, 8, 8, 8, 8, 8, + 649, 649, 170, -1000, 170, 2385, 319, 360, 16, -1000, + 2790, -1000, 217, 2385, 127, -1000, 2790, 170, 203, 203, + 203, -1000, 318, 303, -1000, -1000, 2520, 170, 216, 13, + 463, 3905, 2790, 2103, -1000, -1000, 2790, 367, -1000, -1000, + -1000, 455, 114, 1358, -1000, 1358, 3777, -1000, 140, -1000, + -1000, -79, 7, -1000, -1000, -1000, 297, 317, 209, 3946, + -1000, -1000, -23, 468, -1000, 309, -1000, -1000, 3, -1000, + -1000, -1000, -1000, 245, 1068, 289, -1000, 2918, 2918, -1000, + -1000, 203, 2385, 1954, 114, 721, 376, 56, -1000, 2790, + 118, -1000, -1000, -1000, -1000, 360, -1000, -1000, 359, 3777, + 3777, 455, -1000, 114, -1000, 114, 3777, -1000, 1209, -1000, + 174, -1000, 361, -1000, 42, -1000, -1000, -1000, -1000, -1000, + -1000, -1000, 119, -1000, 111, 1656, 3905, 465, 457, 1507, + -1000, 2918, 1068, 1068, -1000, 170, -1000, 170, 361, -1000, + 361, 364, 361, -51, 361, -53, 361, 361, 361, 361, + 361, 361, -1000, -102, -1000, 114, 2790, -1000, 360, -1000, + 355, 2, -1000, -1000, 162, -1000, -1000, 3777, -1000, -1000, + 428, -1000, 426, 296, 259, -1000, -1000, -1000, -1000, -1000, + 2790, 2790, -1000, 1068, -1000, -1000, -1000, -1000, -1000, -1000, + -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, 170, + 135, 114, 303, 170, 3777, 3777, -1000, -1000, -1000, -1000, + -1000, -1000, 114, 300, -1000, -1000, -1000, -1000, -1000, } var yyPgo = [...]int{ - 0, 695, 34, 431, 693, 690, 689, 688, 685, 682, - 680, 677, 673, 671, 670, 517, 669, 668, 665, 43, - 661, 14, 56, 9, 25, 76, 660, 20, 55, 48, - 658, 29, 657, 655, 42, 653, 707, 651, 650, 44, - 649, 647, 643, 640, 639, 12, 636, 634, 633, 632, - 629, 16, 1, 4, 8, 7, 628, 112, 45, 626, - 18, 625, 621, 614, 612, 6, 611, 30, 610, 13, - 603, 601, 32, 10, 596, 595, 123, 60, 594, 17, - 0, 22, 33, 28, 593, 652, 21, 37, 592, 590, - 589, 587, 586, 585, 579, 578, 577, 576, 38, 572, - 567, 566, 36, 5, 564, 563, 562, 27, 559, 26, - 557, 555, 554, 15, 11, 553, 2, 552, 31, 550, - 549, 548, 121, 3, 542, 541, + 0, 646, 17, 411, 645, 642, 641, 639, 635, 634, + 633, 632, 629, 627, 623, 448, 618, 615, 614, 27, + 609, 16, 92, 8, 29, 3, 607, 21, 69, 41, + 606, 31, 604, 593, 47, 592, 32, 591, 590, 25, + 589, 587, 586, 585, 579, 442, 577, 572, 571, 563, + 552, 13, 4, 2, 6, 7, 551, 65, 46, 549, + 12, 548, 547, 545, 544, 28, 543, 38, 542, 26, + 541, 539, 34, 20, 536, 535, 114, 63, 534, 36, + 0, 11, 37, 15, 533, 620, 14, 42, 532, 531, + 529, 528, 527, 526, 525, 523, 522, 520, 126, 519, + 518, 517, 40, 9, 513, 512, 511, 33, 510, 22, + 509, 508, 506, 10, 5, 505, 1, 504, 35, 503, + 502, 497, 48, 45, 495, 492, } var yyR1 = [...]int{ @@ -987,28 +975,28 @@ var yyR1 = [...]int{ 52, 52, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 48, 48, - 48, 46, 46, 46, 46, 46, 46, 47, 47, 47, - 49, 49, 49, 91, 91, 91, 91, 91, 91, 91, - 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, - 61, 61, 20, 20, 59, 59, 60, 62, 62, 58, - 58, 58, 44, 44, 44, 44, 44, 44, 44, 63, - 63, 64, 64, 65, 65, 66, 66, 67, 68, 68, - 68, 69, 69, 69, 69, 43, 43, 43, 43, 43, - 43, 70, 70, 70, 70, 53, 53, 55, 55, 54, - 56, 72, 72, 73, 76, 76, 77, 77, 74, 74, - 78, 78, 78, 81, 81, 82, 82, 85, 85, 86, - 86, 79, 79, 79, 79, 79, 79, 79, 79, 79, + 48, 46, 46, 46, 46, 47, 47, 47, 49, 49, + 49, 91, 91, 91, 91, 91, 91, 91, 91, 91, + 91, 91, 91, 91, 91, 91, 91, 91, 61, 61, + 20, 20, 59, 59, 60, 62, 62, 58, 58, 58, + 44, 44, 44, 44, 44, 44, 44, 63, 63, 64, + 64, 65, 65, 66, 66, 67, 68, 68, 68, 69, + 69, 69, 69, 43, 43, 43, 43, 43, 43, 70, + 70, 70, 70, 53, 53, 55, 55, 54, 56, 72, + 72, 73, 76, 76, 77, 77, 74, 74, 78, 78, + 78, 81, 81, 82, 82, 85, 85, 86, 86, 79, + 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, - 79, 79, 79, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, - 80, 80, 80, 80, 80, 80, 80, 80, 80, 122, - 123, 124, 90, 90, 90, + 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, + 80, 80, 80, 80, 80, 80, 122, 123, 124, 90, + 90, 90, } var yyR2 = [...]int{ @@ -1038,16 +1026,15 @@ var yyR2 = [...]int{ 1, 3, 1, 1, 1, 1, 1, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 2, 2, 2, 2, 3, 1, 1, 1, 1, 4, 5, - 6, 6, 6, 8, 7, 5, 4, 1, 1, 1, - 4, 4, 4, 2, 1, 2, 2, 2, 1, 2, - 2, 1, 2, 2, 2, 2, 2, 2, 2, 1, - 0, 1, 0, 2, 1, 2, 4, 0, 2, 1, - 3, 5, 1, 1, 1, 1, 1, 1, 1, 0, - 3, 0, 2, 0, 3, 1, 3, 2, 0, 1, - 1, 0, 2, 4, 4, 2, 1, 3, 5, 4, - 6, 1, 3, 3, 5, 1, 3, 1, 2, 3, - 1, 1, 3, 3, 0, 2, 0, 3, 0, 1, - 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 6, 6, 7, 5, 4, 1, 1, 1, 4, 4, + 4, 2, 1, 2, 2, 2, 1, 2, 2, 1, + 2, 2, 2, 2, 2, 2, 2, 1, 0, 1, + 0, 2, 1, 2, 4, 0, 2, 1, 3, 5, + 1, 1, 1, 1, 1, 1, 1, 0, 3, 0, + 2, 0, 3, 1, 3, 2, 0, 1, 1, 0, + 2, 4, 4, 2, 1, 3, 5, 4, 6, 1, + 3, 3, 5, 1, 3, 1, 2, 3, 1, 1, + 3, 3, 0, 2, 0, 3, 0, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, @@ -1059,19 +1046,20 @@ var yyR2 = [...]int{ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 0, 0, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, + 1, 1, } var yyChk = [...]int{ -1000, -120, -1, -2, -6, -7, -8, -9, -10, -11, -13, -14, -3, -4, -18, 8, 9, -12, 93, 94, 95, 108, 23, 109, 112, 6, -122, 7, 149, 39, - -121, 155, -65, 14, -17, 5, -15, -125, -15, -15, + -121, 154, -65, 14, -17, 5, -15, -125, -15, -15, -110, 39, -78, 98, 103, -81, 42, -80, 118, 137, 131, 129, 110, 127, 123, 121, 122, 107, 116, 117, 29, 140, 136, 139, 115, 135, 132, 124, 17, 104, 120, 142, 114, 133, 125, 126, 138, 113, 134, 143, - 154, 130, 128, 144, 98, 98, 99, 93, 99, 98, + 153, 130, 128, 144, 98, 98, 99, 93, 99, 98, -119, 54, -36, -85, 42, -80, -36, -124, -15, -2, 56, -69, 16, 15, -5, -3, -122, 18, -74, 101, -16, -27, -28, -29, -30, -38, -57, -122, -36, 10, @@ -1080,140 +1068,138 @@ var yyChk = [...]int{ 149, -25, -40, 58, -45, 26, 21, -44, -41, -58, -56, -57, 81, 82, 89, 59, 91, -48, -46, -47, -49, 44, 43, 45, 46, 47, 48, 51, 52, 53, - -81, -85, -54, -122, 150, 151, 152, 61, 28, 146, - 147, 148, 102, 87, 149, 42, -80, -66, -67, -25, - -65, -2, -37, 24, -36, 50, 27, 40, -33, -34, - -35, 30, 33, 35, 31, 36, -89, 20, -27, -2, - -122, -88, -87, 20, -85, 44, -36, 41, 40, -92, - -95, -97, -96, -93, -94, 129, 130, 133, 134, 135, - 136, 137, 138, 139, 140, 110, 125, 126, 127, 128, - 113, 114, 115, 116, 117, 118, 120, 121, 122, 123, - 124, -77, -36, 58, 97, 96, -36, 21, -36, -36, - 98, 10, -86, -85, -79, 97, 57, 20, 22, 141, - 60, 15, 61, 93, 35, 147, 148, 146, 151, 26, - 9, 23, 109, 19, 85, 95, 64, 65, 111, 21, - 53, 10, 12, 13, 102, 101, 75, 99, 33, 7, - 91, 24, 72, 30, 25, 31, 73, 16, 87, 36, - 58, 51, 37, 56, 14, 34, 74, 96, 149, 32, - 6, 153, 27, 108, 98, 145, 63, 100, 52, 5, - 103, 8, 38, 28, 62, 11, 112, -22, -24, 83, - -25, -85, 57, 56, 72, 40, 17, -25, -42, 75, - 58, 73, 74, 60, 78, 77, 88, 81, 82, 83, - 84, 85, 86, 87, 79, 80, 66, 67, 68, 69, - 70, 71, 76, -75, -122, -57, -122, -45, -45, -45, - -45, -45, -122, 92, -52, -25, -122, -122, -122, -122, - -61, -25, -122, -122, -122, -122, 40, -68, 22, 23, - -69, -123, -43, 27, 28, -2, -122, -36, -72, -73, - -58, -81, -85, -28, -29, -28, -29, 30, 30, 30, - 34, 30, -34, -85, -123, -123, -2, -87, -39, 11, - -112, -113, -114, -82, 44, 45, -81, -79, -107, -108, - -115, 104, 103, -109, 99, 25, -104, 51, 58, -100, - 143, -98, 39, -98, -98, -98, -98, -98, -102, 39, - -102, -102, -103, 39, -103, 42, 21, -118, 105, 100, - -118, -36, -36, -26, 40, 10, -84, -83, 20, -81, - 44, 92, -25, -25, -50, 51, 58, 52, 53, -25, - -25, -45, -51, -54, -57, 49, 75, 73, 74, 60, + -81, -85, -54, -122, 150, 151, 61, 28, 146, 147, + 148, 102, 87, 149, 42, -80, -66, -67, -25, -65, + -2, -37, 24, -36, 50, 27, 40, -33, -34, -35, + 30, 33, 35, 31, 36, -89, 20, -27, -2, -122, + -88, -87, 20, -85, 44, -36, 41, 40, -92, -95, + -97, -96, -93, -94, 129, 130, 133, 134, 135, 136, + 137, 138, 139, 140, 110, 125, 126, 127, 128, 113, + 114, 115, 116, 117, 118, 120, 121, 122, 123, 124, + -77, -36, 58, 97, 96, -36, 21, -36, -36, 98, + 10, -86, -85, -79, 97, 57, 20, 22, 141, 60, + 15, 61, 93, 35, 147, 148, 146, 26, 9, 23, + 109, 19, 85, 95, 64, 65, 111, 21, 53, 10, + 12, 13, 102, 101, 75, 99, 33, 7, 91, 24, + 72, 30, 25, 31, 73, 16, 87, 36, 58, 51, + 37, 56, 14, 34, 74, 96, 149, 32, 6, 152, + 27, 108, 98, 145, 63, 100, 52, 5, 103, 8, + 38, 28, 62, 11, 112, -22, -24, 83, -25, -85, + 57, 56, 72, 40, 17, -25, -42, 75, 58, 73, + 74, 60, 78, 77, 88, 81, 82, 83, 84, 85, + 86, 87, 79, 80, 66, 67, 68, 69, 70, 71, + 76, -75, -122, -57, -122, -45, -45, -45, -45, -45, + -122, 92, -52, -25, -122, -122, -122, -61, -25, -122, + -122, -122, -122, 40, -68, 22, 23, -69, -123, -43, + 27, 28, -2, -122, -36, -72, -73, -58, -81, -85, + -28, -29, -28, -29, 30, 30, 30, 34, 30, -34, + -85, -123, -123, -2, -87, -39, 11, -112, -113, -114, + -82, 44, 45, -81, -79, -107, -108, -115, 104, 103, + -109, 99, 25, -104, 51, 58, -100, 143, -98, 39, + -98, -98, -98, -98, -98, -102, 39, -102, -102, -103, + 39, -103, 42, 21, -118, 105, 100, -118, -36, -36, + -26, 40, 10, -84, -83, 20, -81, 44, 92, -25, + -25, -50, 51, 58, 52, 53, -25, -25, -45, -51, + -54, -57, 49, 75, 73, 74, 60, -45, -45, -45, -45, -45, -45, -45, -45, -45, -45, -45, -45, -45, - -45, -45, -45, -45, -45, 42, -81, -23, 19, -22, - -82, -86, -79, 40, -123, -25, -58, -19, -59, -60, - 62, -58, -22, -22, -22, -67, -72, -53, -55, -54, - -122, -2, -70, -81, -39, 40, 66, 92, -32, -31, - 37, 38, -31, 30, -123, -65, -25, 40, -114, 66, - 39, 25, -109, 42, 42, -99, 26, 51, -101, 144, - 45, 45, 45, 37, -107, -36, -58, -39, -24, -27, - -83, 83, -86, 51, 52, 53, -51, -45, -45, -45, - -21, 111, 57, -123, -123, -22, -122, 92, -25, 20, - 40, -22, -62, -60, 64, -25, -123, -123, -123, -123, - 40, -123, -123, -123, 40, 92, -65, -73, -25, -82, - -25, 39, -69, -113, -114, -117, -116, -81, 42, -105, - 141, 44, 45, 46, 51, 146, 41, 40, 41, 40, - -36, 100, -63, 12, 92, -21, 57, -45, -45, -123, - -23, -82, -91, 129, 110, 127, 123, 132, 142, 125, - 143, 126, 116, 113, 114, 115, 118, 44, -45, -65, - 65, -25, 63, -55, 28, -2, -122, -81, -81, -69, - -71, -81, 41, 40, -98, -106, 104, 25, 103, 45, - 45, -90, -122, -82, -58, -64, 13, 15, 83, -45, - -123, -123, -98, -98, -103, -98, 117, -98, 117, -98, - -98, -98, -98, -98, -98, -123, 40, -20, 153, -25, - -53, -2, 92, 40, 41, -116, 25, 25, 41, 41, - -25, -52, -45, -123, 44, -123, -81, -81, -123, + -45, -45, 42, -81, -23, 19, -22, -82, -86, -79, + 40, -123, -25, -19, -59, -60, 62, -58, -22, -22, + -22, -67, -72, -53, -55, -54, -122, -2, -70, -81, + -39, 40, 66, 92, -32, -31, 37, 38, -31, 30, + -123, -65, -25, 40, -114, 66, 39, 25, -109, 42, + 42, -99, 26, 51, -101, 144, 45, 45, 45, 37, + -107, -36, -58, -39, -24, -27, -83, 83, -86, 51, + 52, 53, -51, -45, -45, -45, -21, 111, 57, -123, + -123, -22, -122, 92, -25, 20, -22, -62, -60, 64, + -25, -123, -123, -123, -123, 40, -123, -123, -123, 40, + 92, -65, -73, -25, -82, -25, 39, -69, -113, -114, + -117, -116, -81, 42, -105, 141, 44, 45, 46, 51, + 146, 41, 40, 41, 40, -36, 100, -63, 12, 92, + -21, 57, -45, -45, -123, -23, -82, -91, 129, 110, + 127, 123, 132, 142, 125, 143, 126, 116, 113, 114, + 115, 118, 44, -65, 65, -25, 63, -55, 28, -2, + -122, -81, -81, -69, -71, -81, 41, 40, -98, -106, + 104, 25, 103, 45, 45, -90, -122, -82, -58, -64, + 13, 15, 83, -45, -123, -123, -98, -98, -103, -98, + 117, -98, 117, -98, -98, -98, -98, -98, -98, -20, + 152, -25, -53, -2, 92, 40, 41, -116, 25, 25, + 41, 41, -25, -52, -123, 44, -123, -81, -81, } var yyDef = [...]int{ 0, -2, 2, -2, 5, 6, 7, 8, 9, 10, - 11, 12, 313, 0, 130, 130, 130, 0, 350, 0, - 0, 125, 0, 0, 471, 130, 0, 23, 24, 469, - 1, 3, 321, 0, 0, 134, 348, 132, 0, 0, - 28, 0, 0, 346, 351, 352, 353, 354, 433, 434, - 435, 436, 437, 438, 439, 440, 441, 442, 443, 444, - 445, 446, 447, 448, 449, 450, 451, 452, 453, 454, - 455, 456, 457, 458, 459, 460, 461, 462, 463, 464, - 465, 466, 467, 468, 0, 344, 344, 0, 0, 0, - 0, 126, 127, 186, 357, 358, 128, 129, 136, 17, - 0, 13, 0, 0, 313, 19, 0, 135, 0, 349, + 11, 12, 311, 0, 130, 130, 130, 0, 348, 0, + 0, 125, 0, 0, 468, 130, 0, 23, 24, 466, + 1, 3, 319, 0, 0, 134, 346, 132, 0, 0, + 28, 0, 0, 344, 349, 350, 351, 352, 430, 431, + 432, 433, 434, 435, 436, 437, 438, 439, 440, 441, + 442, 443, 444, 445, 446, 447, 448, 449, 450, 451, + 452, 453, 454, 455, 456, 457, 458, 459, 460, 461, + 462, 463, 464, 465, 0, 342, 342, 0, 0, 0, + 0, 126, 127, 186, 355, 356, 128, 129, 136, 17, + 0, 13, 0, 0, 311, 19, 0, 135, 0, 347, 131, 0, 152, 154, 155, 156, -2, 0, 172, 0, - 0, 32, 0, 346, 0, 0, 0, 0, 0, 0, - 0, 0, 122, 123, 0, 0, 0, 137, 18, 470, - 25, 322, 190, 0, 195, 197, 0, 232, 233, 234, + 0, 32, 0, 344, 0, 0, 0, 0, 0, 0, + 0, 0, 122, 123, 0, 0, 0, 137, 18, 467, + 25, 320, 190, 0, 195, 197, 0, 232, 233, 234, 235, 236, 0, 0, 0, 0, 0, 254, 255, 256, - 257, 302, 303, 304, 305, 306, 307, 308, 199, 200, - 299, 0, 340, 0, 0, 0, 0, 290, 0, 267, - 268, 269, 0, 0, 0, -2, -2, 314, 315, 318, - 321, 17, 0, 0, 185, 133, 0, 0, 0, 0, - 0, 177, 0, 0, 0, 0, 0, 171, 0, 17, - 0, 160, 173, 0, 175, 176, 188, 104, 0, 78, - 74, 37, 38, 67, 40, 67, 67, 59, 60, 61, - 62, 63, 64, 65, 66, 52, 67, 67, 67, 56, - 41, 42, 43, 44, 45, 46, 69, 69, 69, 71, - 71, 0, 30, 0, 116, 116, 118, 345, 119, 120, - 0, 0, 187, 359, 360, 361, 362, 363, 364, 365, - 366, 367, 368, 369, 370, 371, 372, 373, 374, 375, - 376, 377, 378, 379, 380, 381, 382, 383, 384, 385, - 386, 387, 388, 389, 390, 391, 392, 393, 394, 395, - 396, 397, 398, 399, 400, 401, 402, 403, 404, 405, - 406, 407, 408, 409, 410, 411, 412, 413, 414, 415, - 416, 417, 418, 419, 420, 421, 422, 423, 424, 425, - 426, 427, 428, 429, 430, 431, 432, 16, 140, 142, - 146, 0, 0, 0, 0, 0, 0, 193, 0, 0, + 257, 300, 301, 302, 303, 304, 305, 306, 199, 200, + 297, 0, 338, 0, 0, 0, 288, 0, 265, 266, + 267, 0, 0, 0, -2, -2, 312, 313, 316, 319, + 17, 0, 0, 185, 133, 0, 0, 0, 0, 0, + 177, 0, 0, 0, 0, 0, 171, 0, 17, 0, + 160, 173, 0, 175, 176, 188, 104, 0, 78, 74, + 37, 38, 67, 40, 67, 67, 59, 60, 61, 62, + 63, 64, 65, 66, 52, 67, 67, 67, 56, 41, + 42, 43, 44, 45, 46, 69, 69, 69, 71, 71, + 0, 30, 0, 116, 116, 118, 343, 119, 120, 0, + 0, 187, 357, 358, 359, 360, 361, 362, 363, 364, + 365, 366, 367, 368, 369, 370, 371, 372, 373, 374, + 375, 376, 377, 378, 379, 380, 381, 382, 383, 384, + 385, 386, 387, 388, 389, 390, 391, 392, 393, 394, + 395, 396, 397, 398, 399, 400, 401, 402, 403, 404, + 405, 406, 407, 408, 409, 410, 411, 412, 413, 414, + 415, 416, 417, 418, 419, 420, 421, 422, 423, 424, + 425, 426, 427, 428, 429, 16, 140, 142, 146, 0, + 0, 0, 0, 0, 0, 193, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 217, 218, 219, 220, - 221, 222, 223, 196, 0, 210, 0, 249, 250, 251, - 252, 0, 138, 0, 0, 230, 0, 0, 0, 136, - 0, 291, 0, 0, 0, 0, 0, 317, 319, 320, - 14, 20, 21, 0, 0, -2, 0, 184, 188, 341, - 0, 299, 0, 153, 168, 0, 165, 178, 179, 180, - 0, 182, 183, 158, 159, 229, 17, 174, 313, 0, - 31, 105, 107, 110, 111, 112, 355, 356, 33, 34, - 0, 0, 0, 0, 99, 100, 81, 79, 0, 76, - 75, 39, 0, 57, 58, 53, 54, 55, 47, 0, - 48, 49, 50, 0, 51, 0, 347, 0, 117, 0, - 0, 121, 124, 188, 0, 0, 143, 147, 0, 149, - 150, 0, 191, 192, 194, 211, 0, 213, 215, 323, - 324, 201, 202, 226, 227, 228, 0, 0, 0, 0, - 224, 206, 0, 237, 238, 239, 240, 241, 242, 243, - 244, 245, 246, 247, 248, 0, 253, 0, 0, 139, - 300, 0, -2, 0, 339, 0, 0, 0, 297, 294, - 0, 0, 0, 0, 0, 316, 22, 325, 335, 337, - 0, 17, 0, 331, 313, 0, 0, 0, 163, 169, - 0, 0, 164, 181, -2, 321, 189, 0, 108, 0, - 0, 95, 0, 97, 98, 87, 0, 80, 36, 77, - 0, 0, 0, 0, 113, 114, 0, 309, 141, 151, - 148, 144, 0, 212, 214, 216, 203, 224, 207, 0, - 204, 0, 0, 198, 258, 0, 138, 0, 231, 0, - 0, 313, 0, 295, 0, 0, 266, 270, 271, 272, - 0, 338, -2, 0, 0, 0, 321, 342, 343, 300, - 166, 0, 27, 106, 109, 0, 101, 67, 96, 89, - 88, 82, 83, 84, 85, 86, 68, 0, 72, 0, - 472, 0, 311, 0, 0, 205, 0, 225, 208, 259, - 0, 301, 0, 67, 274, 67, 71, 67, 278, 67, - 281, 67, 67, 67, 67, 67, 67, 289, 0, 292, - 265, 298, 0, 336, 0, -2, 0, 333, 332, 26, - 0, 161, 94, 0, 103, 35, 0, 91, 93, 0, - 0, 29, 473, 474, 115, 15, 0, 0, 145, 209, - 260, 261, 273, 275, 276, 277, 279, 280, 282, 283, - 284, 285, 286, 287, 288, 262, 0, 0, 0, 296, - 328, 17, 0, 0, 167, 102, 90, 92, 70, 73, - 312, 310, 0, 264, 293, -2, 334, 162, 263, + 0, 0, 0, 0, 217, 218, 219, 220, 221, 222, + 223, 196, 0, 210, 0, 249, 250, 251, 252, 0, + 138, 0, 0, 230, 0, 0, 136, 0, 289, 0, + 0, 0, 0, 0, 315, 317, 318, 14, 20, 21, + 0, 0, -2, 0, 184, 188, 339, 0, 297, 0, + 153, 168, 0, 165, 178, 179, 180, 0, 182, 183, + 158, 159, 229, 17, 174, 311, 0, 31, 105, 107, + 110, 111, 112, 353, 354, 33, 34, 0, 0, 0, + 0, 99, 100, 81, 79, 0, 76, 75, 39, 0, + 57, 58, 53, 54, 55, 47, 0, 48, 49, 50, + 0, 51, 0, 345, 0, 117, 0, 0, 121, 124, + 188, 0, 0, 143, 147, 0, 149, 150, 0, 191, + 192, 194, 211, 0, 213, 215, 321, 322, 201, 202, + 226, 227, 228, 0, 0, 0, 0, 224, 206, 0, + 237, 238, 239, 240, 241, 242, 243, 244, 245, 246, + 247, 248, 0, 253, 0, 0, 139, 298, 0, -2, + 0, 337, 0, 0, 295, 292, 0, 0, 0, 0, + 0, 314, 22, 323, 333, 335, 0, 17, 0, 329, + 311, 0, 0, 0, 163, 169, 0, 0, 164, 181, + -2, 319, 189, 0, 108, 0, 0, 95, 0, 97, + 98, 87, 0, 80, 36, 77, 0, 0, 0, 0, + 113, 114, 0, 307, 141, 151, 148, 144, 0, 212, + 214, 216, 203, 224, 207, 0, 204, 0, 0, 198, + 258, 0, 138, 0, 231, 0, 311, 0, 293, 0, + 0, 264, 268, 269, 270, 0, 336, -2, 0, 0, + 0, 319, 340, 341, 298, 166, 0, 27, 106, 109, + 0, 101, 67, 96, 89, 88, 82, 83, 84, 85, + 86, 68, 0, 72, 0, 469, 0, 309, 0, 0, + 205, 0, 225, 208, 259, 0, 299, 0, 67, 272, + 67, 71, 67, 276, 67, 279, 67, 67, 67, 67, + 67, 67, 287, 290, 263, 296, 0, 334, 0, -2, + 0, 331, 330, 26, 0, 161, 94, 0, 103, 35, + 0, 91, 93, 0, 0, 29, 470, 471, 115, 15, + 0, 0, 145, 209, 260, 261, 271, 273, 274, 275, + 277, 278, 280, 281, 282, 283, 284, 285, 286, 0, + 0, 294, 326, 17, 0, 0, 167, 102, 90, 92, + 70, 73, 310, 308, 262, 291, -2, 332, 162, } var yyTok1 = [...]int{ @@ -1222,7 +1208,7 @@ var yyTok1 = [...]int{ 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 59, 3, 3, 3, 86, 78, 3, 39, 41, 83, 81, 40, 82, 92, 84, 3, 3, - 3, 3, 3, 3, 3, 3, 3, 3, 3, 155, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 154, 67, 66, 68, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, @@ -1246,7 +1232,7 @@ var yyTok2 = [...]int{ 119, 120, 121, 122, 123, 124, 125, 126, 127, 128, 129, 130, 131, 132, 133, 134, 135, 136, 137, 138, 139, 140, 141, 142, 143, 144, 145, 146, 147, 148, - 149, 150, 151, 152, 153, 154, + 149, 150, 151, 152, 153, } var yyTok3 = [...]int{ 0, @@ -1591,29 +1577,29 @@ yydefault: case 1: yyDollar = yyS[yypt-2 : yypt+1] - //line sql.y:244 + //line sql.y:243 { setParseTree(yylex, yyDollar[1].statement) } case 2: yyDollar = yyS[yypt-0 : yypt+1] - //line sql.y:249 + //line sql.y:248 { } case 3: yyDollar = yyS[yypt-1 : yypt+1] - //line sql.y:250 + //line sql.y:249 { } case 4: yyDollar = yyS[yypt-1 : yypt+1] - //line sql.y:254 + //line sql.y:253 { yyVAL.statement = yyDollar[1].selStmt } case 13: yyDollar = yyS[yypt-3 : yypt+1] - //line sql.y:268 + //line sql.y:267 { sel := yyDollar[1].selStmt.(*Select) sel.OrderBy = yyDollar[2].orderBy @@ -1622,49 +1608,49 @@ yydefault: } case 14: yyDollar = yyS[yypt-5 : yypt+1] - //line sql.y:275 + //line sql.y:274 { yyVAL.selStmt = &Union{Type: yyDollar[2].str, Left: yyDollar[1].selStmt, Right: yyDollar[3].selStmt, OrderBy: yyDollar[4].orderBy, Limit: yyDollar[5].limit} } case 15: yyDollar = yyS[yypt-8 : yypt+1] - //line sql.y:282 + //line sql.y:281 { yyVAL.selStmt = &Select{Comments: Comments(yyDollar[2].bytes2), Distinct: yyDollar[3].str, SelectExprs: yyDollar[4].selectExprs, From: yyDollar[5].tableExprs, Where: NewWhere(WhereStr, yyDollar[6].expr), GroupBy: GroupBy(yyDollar[7].exprs), Having: NewWhere(HavingStr, yyDollar[8].expr)} } case 16: yyDollar = yyS[yypt-4 : yypt+1] - //line sql.y:286 + //line sql.y:285 { yyVAL.selStmt = &Select{Comments: Comments(yyDollar[2].bytes2), Distinct: yyDollar[3].str, SelectExprs: yyDollar[4].selectExprs} } case 17: yyDollar = yyS[yypt-1 : yypt+1] - //line sql.y:292 + //line sql.y:291 { yyVAL.selStmt = yyDollar[1].selStmt } case 18: yyDollar = yyS[yypt-3 : yypt+1] - //line sql.y:296 + //line sql.y:295 { yyVAL.selStmt = &ParenSelect{Select: yyDollar[2].selStmt} } case 19: yyDollar = yyS[yypt-1 : yypt+1] - //line sql.y:302 + //line sql.y:301 { yyVAL.selStmt = yyDollar[1].selStmt } case 20: yyDollar = yyS[yypt-3 : yypt+1] - //line sql.y:306 + //line sql.y:305 { yyVAL.selStmt = &ParenSelect{Select: yyDollar[2].selStmt} } case 21: yyDollar = yyS[yypt-5 : yypt+1] - //line sql.y:313 + //line sql.y:312 { // insert_data returns a *Insert pre-filled with Columns & Values ins := yyDollar[5].ins @@ -1676,7 +1662,7 @@ yydefault: } case 22: yyDollar = yyS[yypt-6 : yypt+1] - //line sql.y:323 + //line sql.y:322 { cols := make(Columns, 0, len(yyDollar[6].updateExprs)) vals := make(ValTuple, 0, len(yyDollar[6].updateExprs)) @@ -1688,84 +1674,84 @@ yydefault: } case 23: yyDollar = yyS[yypt-1 : yypt+1] - //line sql.y:335 + //line sql.y:334 { yyVAL.str = InsertStr } case 24: yyDollar = yyS[yypt-1 : yypt+1] - //line sql.y:339 + //line sql.y:338 { yyVAL.str = ReplaceStr } case 25: yyDollar = yyS[yypt-3 : yypt+1] - //line sql.y:343 + //line sql.y:342 { yyVAL.str = ReplaceStr } case 26: yyDollar = yyS[yypt-8 : yypt+1] - //line sql.y:349 + //line sql.y:348 { yyVAL.statement = &Update{Comments: Comments(yyDollar[2].bytes2), TableExprs: yyDollar[3].tableExprs, Exprs: yyDollar[5].updateExprs, Where: NewWhere(WhereStr, yyDollar[6].expr), OrderBy: yyDollar[7].orderBy, Limit: yyDollar[8].limit} } case 27: yyDollar = yyS[yypt-7 : yypt+1] - //line sql.y:355 + //line sql.y:354 { yyVAL.statement = &Delete{Comments: Comments(yyDollar[2].bytes2), TableExprs: TableExprs{&AliasedTableExpr{Expr: yyDollar[4].tableName}}, Where: NewWhere(WhereStr, yyDollar[5].expr), OrderBy: yyDollar[6].orderBy, Limit: yyDollar[7].limit} } case 28: yyDollar = yyS[yypt-2 : yypt+1] - //line sql.y:361 + //line sql.y:360 { yyDollar[1].ddl.TableSpec = yyDollar[2].TableSpec yyVAL.statement = yyDollar[1].ddl } case 29: yyDollar = yyS[yypt-8 : yypt+1] - //line sql.y:366 + //line sql.y:365 { // Change this to an alter statement yyVAL.statement = &DDL{Action: CreateIndexStr, Table: yyDollar[7].tableName, NewName: yyDollar[7].tableName} } case 30: yyDollar = yyS[yypt-4 : yypt+1] - //line sql.y:373 + //line sql.y:372 { yyVAL.ddl = &DDL{Action: CreateStr, NewName: yyDollar[4].tableName} setDDL(yylex, yyVAL.ddl) } case 31: yyDollar = yyS[yypt-4 : yypt+1] - //line sql.y:380 + //line sql.y:379 { yyVAL.TableSpec = yyDollar[2].TableSpec yyVAL.TableSpec.Options = yyDollar[4].str } case 32: yyDollar = yyS[yypt-1 : yypt+1] - //line sql.y:387 + //line sql.y:386 { yyVAL.TableSpec = &TableSpec{} yyVAL.TableSpec.AddColumn(yyDollar[1].columnDefinition) } case 33: yyDollar = yyS[yypt-3 : yypt+1] - //line sql.y:392 + //line sql.y:391 { yyVAL.TableSpec.AddColumn(yyDollar[3].columnDefinition) } case 34: yyDollar = yyS[yypt-3 : yypt+1] - //line sql.y:396 + //line sql.y:395 { yyVAL.TableSpec.AddIndex(yyDollar[3].indexDefinition) } case 35: yyDollar = yyS[yypt-6 : yypt+1] - //line sql.y:402 + //line sql.y:401 { yyDollar[2].columnType.NotNull = yyDollar[3].boolVal yyDollar[2].columnType.Default = yyDollar[4].optVal @@ -1775,7 +1761,7 @@ yydefault: } case 36: yyDollar = yyS[yypt-3 : yypt+1] - //line sql.y:411 + //line sql.y:410 { yyVAL.columnType = yyDollar[1].columnType yyVAL.columnType.Unsigned = yyDollar[2].boolVal @@ -1783,56 +1769,56 @@ yydefault: } case 39: yyDollar = yyS[yypt-2 : yypt+1] - //line sql.y:421 + //line sql.y:420 { yyVAL.columnType = yyDollar[1].columnType yyVAL.columnType.Length = yyDollar[2].optVal } case 40: yyDollar = yyS[yypt-1 : yypt+1] - //line sql.y:426 + //line sql.y:425 { yyVAL.columnType = yyDollar[1].columnType } case 41: yyDollar = yyS[yypt-1 : yypt+1] - //line sql.y:432 + //line sql.y:431 { yyVAL.columnType = ColumnType{Type: string(yyDollar[1].bytes)} } case 42: yyDollar = yyS[yypt-1 : yypt+1] - //line sql.y:436 + //line sql.y:435 { yyVAL.columnType = ColumnType{Type: string(yyDollar[1].bytes)} } case 43: yyDollar = yyS[yypt-1 : yypt+1] - //line sql.y:440 + //line sql.y:439 { yyVAL.columnType = ColumnType{Type: string(yyDollar[1].bytes)} } case 44: yyDollar = yyS[yypt-1 : yypt+1] - //line sql.y:444 + //line sql.y:443 { yyVAL.columnType = ColumnType{Type: string(yyDollar[1].bytes)} } case 45: yyDollar = yyS[yypt-1 : yypt+1] - //line sql.y:448 + //line sql.y:447 { yyVAL.columnType = ColumnType{Type: string(yyDollar[1].bytes)} } case 46: yyDollar = yyS[yypt-1 : yypt+1] - //line sql.y:452 + //line sql.y:451 { yyVAL.columnType = ColumnType{Type: string(yyDollar[1].bytes)} } case 47: yyDollar = yyS[yypt-2 : yypt+1] - //line sql.y:458 + //line sql.y:457 { yyVAL.columnType = ColumnType{Type: string(yyDollar[1].bytes)} yyVAL.columnType.Length = yyDollar[2].LengthScaleOption.Length @@ -1840,7 +1826,7 @@ yydefault: } case 48: yyDollar = yyS[yypt-2 : yypt+1] - //line sql.y:464 + //line sql.y:463 { yyVAL.columnType = ColumnType{Type: string(yyDollar[1].bytes)} yyVAL.columnType.Length = yyDollar[2].LengthScaleOption.Length @@ -1848,7 +1834,7 @@ yydefault: } case 49: yyDollar = yyS[yypt-2 : yypt+1] - //line sql.y:470 + //line sql.y:469 { yyVAL.columnType = ColumnType{Type: string(yyDollar[1].bytes)} yyVAL.columnType.Length = yyDollar[2].LengthScaleOption.Length @@ -1856,7 +1842,7 @@ yydefault: } case 50: yyDollar = yyS[yypt-2 : yypt+1] - //line sql.y:476 + //line sql.y:475 { yyVAL.columnType = ColumnType{Type: string(yyDollar[1].bytes)} yyVAL.columnType.Length = yyDollar[2].LengthScaleOption.Length @@ -1864,7 +1850,7 @@ yydefault: } case 51: yyDollar = yyS[yypt-2 : yypt+1] - //line sql.y:482 + //line sql.y:481 { yyVAL.columnType = ColumnType{Type: string(yyDollar[1].bytes)} yyVAL.columnType.Length = yyDollar[2].LengthScaleOption.Length @@ -1872,115 +1858,115 @@ yydefault: } case 52: yyDollar = yyS[yypt-1 : yypt+1] - //line sql.y:490 + //line sql.y:489 { yyVAL.columnType = ColumnType{Type: string(yyDollar[1].bytes)} } case 53: yyDollar = yyS[yypt-2 : yypt+1] - //line sql.y:494 + //line sql.y:493 { yyVAL.columnType = ColumnType{Type: string(yyDollar[1].bytes), Length: yyDollar[2].optVal} } case 54: yyDollar = yyS[yypt-2 : yypt+1] - //line sql.y:498 + //line sql.y:497 { yyVAL.columnType = ColumnType{Type: string(yyDollar[1].bytes), Length: yyDollar[2].optVal} } case 55: yyDollar = yyS[yypt-2 : yypt+1] - //line sql.y:502 + //line sql.y:501 { yyVAL.columnType = ColumnType{Type: string(yyDollar[1].bytes), Length: yyDollar[2].optVal} } case 56: yyDollar = yyS[yypt-1 : yypt+1] - //line sql.y:506 + //line sql.y:505 { yyVAL.columnType = ColumnType{Type: string(yyDollar[1].bytes)} } case 57: yyDollar = yyS[yypt-2 : yypt+1] - //line sql.y:512 + //line sql.y:511 { yyVAL.columnType = ColumnType{Type: string(yyDollar[1].bytes), Length: yyDollar[2].optVal} } case 58: yyDollar = yyS[yypt-2 : yypt+1] - //line sql.y:516 + //line sql.y:515 { yyVAL.columnType = ColumnType{Type: string(yyDollar[1].bytes), Length: yyDollar[2].optVal} } case 59: yyDollar = yyS[yypt-1 : yypt+1] - //line sql.y:520 + //line sql.y:519 { yyVAL.columnType = ColumnType{Type: string(yyDollar[1].bytes)} } case 60: yyDollar = yyS[yypt-1 : yypt+1] - //line sql.y:524 + //line sql.y:523 { yyVAL.columnType = ColumnType{Type: string(yyDollar[1].bytes)} } case 61: yyDollar = yyS[yypt-1 : yypt+1] - //line sql.y:528 + //line sql.y:527 { yyVAL.columnType = ColumnType{Type: string(yyDollar[1].bytes)} } case 62: yyDollar = yyS[yypt-1 : yypt+1] - //line sql.y:532 + //line sql.y:531 { yyVAL.columnType = ColumnType{Type: string(yyDollar[1].bytes)} } case 63: yyDollar = yyS[yypt-1 : yypt+1] - //line sql.y:536 + //line sql.y:535 { yyVAL.columnType = ColumnType{Type: string(yyDollar[1].bytes)} } case 64: yyDollar = yyS[yypt-1 : yypt+1] - //line sql.y:540 + //line sql.y:539 { yyVAL.columnType = ColumnType{Type: string(yyDollar[1].bytes)} } case 65: yyDollar = yyS[yypt-1 : yypt+1] - //line sql.y:544 + //line sql.y:543 { yyVAL.columnType = ColumnType{Type: string(yyDollar[1].bytes)} } case 66: yyDollar = yyS[yypt-1 : yypt+1] - //line sql.y:548 + //line sql.y:547 { yyVAL.columnType = ColumnType{Type: string(yyDollar[1].bytes)} } case 67: yyDollar = yyS[yypt-0 : yypt+1] - //line sql.y:553 + //line sql.y:552 { yyVAL.optVal = nil } case 68: yyDollar = yyS[yypt-3 : yypt+1] - //line sql.y:557 + //line sql.y:556 { yyVAL.optVal = NewIntVal(yyDollar[2].bytes) } case 69: yyDollar = yyS[yypt-0 : yypt+1] - //line sql.y:562 + //line sql.y:561 { yyVAL.LengthScaleOption = LengthScaleOption{} } case 70: yyDollar = yyS[yypt-5 : yypt+1] - //line sql.y:566 + //line sql.y:565 { yyVAL.LengthScaleOption = LengthScaleOption{ Length: NewIntVal(yyDollar[2].bytes), @@ -1989,13 +1975,13 @@ yydefault: } case 71: yyDollar = yyS[yypt-0 : yypt+1] - //line sql.y:574 + //line sql.y:573 { yyVAL.LengthScaleOption = LengthScaleOption{} } case 72: yyDollar = yyS[yypt-3 : yypt+1] - //line sql.y:578 + //line sql.y:577 { yyVAL.LengthScaleOption = LengthScaleOption{ Length: NewIntVal(yyDollar[2].bytes), @@ -2003,7 +1989,7 @@ yydefault: } case 73: yyDollar = yyS[yypt-5 : yypt+1] - //line sql.y:584 + //line sql.y:583 { yyVAL.LengthScaleOption = LengthScaleOption{ Length: NewIntVal(yyDollar[2].bytes), @@ -2012,266 +1998,266 @@ yydefault: } case 74: yyDollar = yyS[yypt-0 : yypt+1] - //line sql.y:592 + //line sql.y:591 { yyVAL.boolVal = BoolVal(false) } case 75: yyDollar = yyS[yypt-1 : yypt+1] - //line sql.y:596 + //line sql.y:595 { yyVAL.boolVal = BoolVal(true) } case 76: yyDollar = yyS[yypt-0 : yypt+1] - //line sql.y:601 + //line sql.y:600 { yyVAL.boolVal = BoolVal(false) } case 77: yyDollar = yyS[yypt-1 : yypt+1] - //line sql.y:605 + //line sql.y:604 { yyVAL.boolVal = BoolVal(true) } case 78: yyDollar = yyS[yypt-0 : yypt+1] - //line sql.y:611 + //line sql.y:610 { yyVAL.boolVal = BoolVal(false) } case 79: yyDollar = yyS[yypt-1 : yypt+1] - //line sql.y:615 + //line sql.y:614 { yyVAL.boolVal = BoolVal(false) } case 80: yyDollar = yyS[yypt-2 : yypt+1] - //line sql.y:619 + //line sql.y:618 { yyVAL.boolVal = BoolVal(true) } case 81: yyDollar = yyS[yypt-0 : yypt+1] - //line sql.y:624 + //line sql.y:623 { yyVAL.optVal = nil } case 82: yyDollar = yyS[yypt-2 : yypt+1] - //line sql.y:628 + //line sql.y:627 { yyVAL.optVal = NewStrVal(yyDollar[2].bytes) } case 83: yyDollar = yyS[yypt-2 : yypt+1] - //line sql.y:632 + //line sql.y:631 { yyVAL.optVal = NewIntVal(yyDollar[2].bytes) } case 84: yyDollar = yyS[yypt-2 : yypt+1] - //line sql.y:636 + //line sql.y:635 { yyVAL.optVal = NewFloatVal(yyDollar[2].bytes) } case 85: yyDollar = yyS[yypt-2 : yypt+1] - //line sql.y:640 + //line sql.y:639 { yyVAL.optVal = NewValArg(yyDollar[2].bytes) } case 86: yyDollar = yyS[yypt-2 : yypt+1] - //line sql.y:644 + //line sql.y:643 { yyVAL.optVal = NewValArg(yyDollar[2].bytes) } case 87: yyDollar = yyS[yypt-0 : yypt+1] - //line sql.y:649 + //line sql.y:648 { yyVAL.boolVal = BoolVal(false) } case 88: yyDollar = yyS[yypt-1 : yypt+1] - //line sql.y:653 + //line sql.y:652 { yyVAL.boolVal = BoolVal(true) } case 89: yyDollar = yyS[yypt-0 : yypt+1] - //line sql.y:658 + //line sql.y:657 { yyVAL.colKeyOpt = colKeyNone } case 90: yyDollar = yyS[yypt-2 : yypt+1] - //line sql.y:662 + //line sql.y:661 { yyVAL.colKeyOpt = colKeyPrimary } case 91: yyDollar = yyS[yypt-1 : yypt+1] - //line sql.y:666 + //line sql.y:665 { yyVAL.colKeyOpt = colKey } case 92: yyDollar = yyS[yypt-2 : yypt+1] - //line sql.y:670 + //line sql.y:669 { yyVAL.colKeyOpt = colKeyUniqueKey } case 93: yyDollar = yyS[yypt-1 : yypt+1] - //line sql.y:674 + //line sql.y:673 { yyVAL.colKeyOpt = colKeyUnique } case 94: yyDollar = yyS[yypt-4 : yypt+1] - //line sql.y:680 + //line sql.y:679 { yyVAL.indexDefinition = &IndexDefinition{Info: yyDollar[1].indexInfo, Columns: yyDollar[3].indexColumns} } case 95: yyDollar = yyS[yypt-2 : yypt+1] - //line sql.y:686 + //line sql.y:685 { yyVAL.indexInfo = &IndexInfo{Type: string(yyDollar[1].bytes) + " " + string(yyDollar[2].bytes), Name: NewColIdent("PRIMARY"), Primary: true, Unique: true} } case 96: yyDollar = yyS[yypt-3 : yypt+1] - //line sql.y:690 + //line sql.y:689 { yyVAL.indexInfo = &IndexInfo{Type: string(yyDollar[1].bytes) + " " + string(yyDollar[2].str), Name: NewColIdent(string(yyDollar[3].bytes)), Unique: true} } case 97: yyDollar = yyS[yypt-2 : yypt+1] - //line sql.y:694 + //line sql.y:693 { yyVAL.indexInfo = &IndexInfo{Type: string(yyDollar[1].bytes), Name: NewColIdent(string(yyDollar[2].bytes)), Unique: true} } case 98: yyDollar = yyS[yypt-2 : yypt+1] - //line sql.y:698 + //line sql.y:697 { yyVAL.indexInfo = &IndexInfo{Type: string(yyDollar[1].str), Name: NewColIdent(string(yyDollar[2].bytes)), Unique: false} } case 99: yyDollar = yyS[yypt-1 : yypt+1] - //line sql.y:704 + //line sql.y:703 { yyVAL.str = string(yyDollar[1].bytes) } case 100: yyDollar = yyS[yypt-1 : yypt+1] - //line sql.y:708 + //line sql.y:707 { yyVAL.str = string(yyDollar[1].bytes) } case 101: yyDollar = yyS[yypt-1 : yypt+1] - //line sql.y:714 + //line sql.y:713 { yyVAL.indexColumns = []*IndexColumn{yyDollar[1].indexColumn} } case 102: yyDollar = yyS[yypt-3 : yypt+1] - //line sql.y:718 + //line sql.y:717 { yyVAL.indexColumns = append(yyVAL.indexColumns, yyDollar[3].indexColumn) } case 103: yyDollar = yyS[yypt-2 : yypt+1] - //line sql.y:724 + //line sql.y:723 { yyVAL.indexColumn = &IndexColumn{Column: yyDollar[1].colIdent, Length: yyDollar[2].optVal} } case 104: yyDollar = yyS[yypt-0 : yypt+1] - //line sql.y:729 + //line sql.y:728 { yyVAL.str = "" } case 105: yyDollar = yyS[yypt-1 : yypt+1] - //line sql.y:733 + //line sql.y:732 { yyVAL.str = " " + string(yyDollar[1].str) } case 106: yyDollar = yyS[yypt-3 : yypt+1] - //line sql.y:737 + //line sql.y:736 { yyVAL.str = string(yyDollar[1].str) + ", " + string(yyDollar[3].str) } case 107: yyDollar = yyS[yypt-1 : yypt+1] - //line sql.y:745 + //line sql.y:744 { yyVAL.str = yyDollar[1].str } case 108: yyDollar = yyS[yypt-2 : yypt+1] - //line sql.y:749 + //line sql.y:748 { yyVAL.str = yyDollar[1].str + " " + yyDollar[2].str } case 109: yyDollar = yyS[yypt-3 : yypt+1] - //line sql.y:753 + //line sql.y:752 { yyVAL.str = yyDollar[1].str + "=" + yyDollar[3].str } case 110: yyDollar = yyS[yypt-1 : yypt+1] - //line sql.y:759 + //line sql.y:758 { yyVAL.str = yyDollar[1].colIdent.String() } case 111: yyDollar = yyS[yypt-1 : yypt+1] - //line sql.y:763 + //line sql.y:762 { yyVAL.str = "'" + string(yyDollar[1].bytes) + "'" } case 112: yyDollar = yyS[yypt-1 : yypt+1] - //line sql.y:767 + //line sql.y:766 { yyVAL.str = string(yyDollar[1].bytes) } case 113: yyDollar = yyS[yypt-6 : yypt+1] - //line sql.y:773 + //line sql.y:772 { yyVAL.statement = &DDL{Action: AlterStr, Table: yyDollar[3].tableName, NewName: yyDollar[3].tableName} } case 114: yyDollar = yyS[yypt-6 : yypt+1] - //line sql.y:777 + //line sql.y:776 { // Change this to a rename statement yyVAL.statement = &DDL{Action: RenameStr, Table: yyDollar[3].tableName, NewName: yyDollar[6].tableName} } case 115: yyDollar = yyS[yypt-8 : yypt+1] - //line sql.y:782 + //line sql.y:781 { // Rename an index can just be an alter yyVAL.statement = &DDL{Action: AlterStr, Table: yyDollar[3].tableName, NewName: yyDollar[3].tableName} } case 116: yyDollar = yyS[yypt-0 : yypt+1] - //line sql.y:788 + //line sql.y:787 { } case 118: yyDollar = yyS[yypt-4 : yypt+1] - //line sql.y:792 + //line sql.y:791 { var exists bool if yyDollar[3].byt != 0 { @@ -2281,7 +2267,7 @@ yydefault: } case 119: yyDollar = yyS[yypt-4 : yypt+1] - //line sql.y:800 + //line sql.y:799 { var exists bool if yyDollar[3].byt != 0 { @@ -2291,758 +2277,758 @@ yydefault: } case 120: yyDollar = yyS[yypt-4 : yypt+1] - //line sql.y:810 + //line sql.y:809 { yyVAL.statement = &Show{Type: string(yyDollar[3].bytes), ShowCreate: true, OnTable: yyDollar[4].tableName} } case 121: yyDollar = yyS[yypt-5 : yypt+1] - //line sql.y:814 + //line sql.y:813 { yyVAL.statement = &Show{Type: string(yyDollar[2].bytes), OnTable: yyDollar[5].tableName} } case 122: yyDollar = yyS[yypt-3 : yypt+1] - //line sql.y:818 + //line sql.y:817 { yyVAL.statement = &Show{Type: string(yyDollar[2].bytes), OnTable: yyDollar[3].tableName} } case 123: yyDollar = yyS[yypt-3 : yypt+1] - //line sql.y:822 + //line sql.y:821 { yyVAL.statement = &Show{Type: string(yyDollar[3].bytes)} } case 124: yyDollar = yyS[yypt-5 : yypt+1] - //line sql.y:826 + //line sql.y:825 { yyVAL.statement = &Show{Type: "table", OnTable: yyDollar[5].tableName} } case 125: yyDollar = yyS[yypt-0 : yypt+1] - //line sql.y:831 + //line sql.y:830 { yyVAL.bytes = nil } case 126: yyDollar = yyS[yypt-1 : yypt+1] - //line sql.y:835 + //line sql.y:834 { yyVAL.bytes = nil } case 127: yyDollar = yyS[yypt-2 : yypt+1] - //line sql.y:841 + //line sql.y:840 { yyVAL.statement = &Show{Type: "table", OnTable: yyDollar[2].tableName} } case 128: yyDollar = yyS[yypt-2 : yypt+1] - //line sql.y:845 + //line sql.y:844 { yyVAL.statement = &Show{Type: "table", OnTable: yyDollar[2].tableName} } case 129: yyDollar = yyS[yypt-2 : yypt+1] - //line sql.y:849 + //line sql.y:848 { yyVAL.statement = &Explain{} } case 130: yyDollar = yyS[yypt-0 : yypt+1] - //line sql.y:854 + //line sql.y:853 { setAllowComments(yylex, true) } case 131: yyDollar = yyS[yypt-2 : yypt+1] - //line sql.y:858 + //line sql.y:857 { yyVAL.bytes2 = yyDollar[2].bytes2 setAllowComments(yylex, false) } case 132: yyDollar = yyS[yypt-0 : yypt+1] - //line sql.y:864 + //line sql.y:863 { yyVAL.bytes2 = nil } case 133: yyDollar = yyS[yypt-2 : yypt+1] - //line sql.y:868 + //line sql.y:867 { yyVAL.bytes2 = append(yyDollar[1].bytes2, yyDollar[2].bytes) } case 134: yyDollar = yyS[yypt-1 : yypt+1] - //line sql.y:874 + //line sql.y:873 { yyVAL.str = UnionStr } case 135: yyDollar = yyS[yypt-2 : yypt+1] - //line sql.y:878 + //line sql.y:877 { yyVAL.str = UnionAllStr } case 136: yyDollar = yyS[yypt-0 : yypt+1] - //line sql.y:883 + //line sql.y:882 { yyVAL.str = "" } case 137: yyDollar = yyS[yypt-1 : yypt+1] - //line sql.y:887 + //line sql.y:886 { yyVAL.str = DistinctStr } case 138: yyDollar = yyS[yypt-0 : yypt+1] - //line sql.y:892 + //line sql.y:891 { yyVAL.selectExprs = nil } case 139: yyDollar = yyS[yypt-1 : yypt+1] - //line sql.y:896 + //line sql.y:895 { yyVAL.selectExprs = yyDollar[1].selectExprs } case 140: yyDollar = yyS[yypt-1 : yypt+1] - //line sql.y:902 + //line sql.y:901 { yyVAL.selectExprs = SelectExprs{yyDollar[1].selectExpr} } case 141: yyDollar = yyS[yypt-3 : yypt+1] - //line sql.y:906 + //line sql.y:905 { yyVAL.selectExprs = append(yyVAL.selectExprs, yyDollar[3].selectExpr) } case 142: yyDollar = yyS[yypt-1 : yypt+1] - //line sql.y:912 + //line sql.y:911 { yyVAL.selectExpr = &StarExpr{} } case 143: yyDollar = yyS[yypt-2 : yypt+1] - //line sql.y:916 + //line sql.y:915 { yyVAL.selectExpr = &AliasedExpr{Expr: yyDollar[1].expr, As: yyDollar[2].colIdent} } case 144: yyDollar = yyS[yypt-3 : yypt+1] - //line sql.y:920 + //line sql.y:919 { yyVAL.selectExpr = &StarExpr{TableName: TableName{Name: yyDollar[1].tableIdent}} } case 145: yyDollar = yyS[yypt-5 : yypt+1] - //line sql.y:924 + //line sql.y:923 { yyVAL.selectExpr = &StarExpr{TableName: TableName{Qualifier: yyDollar[1].tableIdent, Name: yyDollar[3].tableIdent}} } case 146: yyDollar = yyS[yypt-0 : yypt+1] - //line sql.y:929 + //line sql.y:928 { yyVAL.colIdent = ColIdent{} } case 147: yyDollar = yyS[yypt-1 : yypt+1] - //line sql.y:933 + //line sql.y:932 { yyVAL.colIdent = yyDollar[1].colIdent } case 148: yyDollar = yyS[yypt-2 : yypt+1] - //line sql.y:937 + //line sql.y:936 { yyVAL.colIdent = yyDollar[2].colIdent } case 150: yyDollar = yyS[yypt-1 : yypt+1] - //line sql.y:944 + //line sql.y:943 { yyVAL.colIdent = NewColIdent(string(yyDollar[1].bytes)) } case 151: yyDollar = yyS[yypt-2 : yypt+1] - //line sql.y:950 + //line sql.y:949 { yyVAL.tableExprs = yyDollar[2].tableExprs } case 152: yyDollar = yyS[yypt-1 : yypt+1] - //line sql.y:956 + //line sql.y:955 { yyVAL.tableExprs = TableExprs{yyDollar[1].tableExpr} } case 153: yyDollar = yyS[yypt-3 : yypt+1] - //line sql.y:960 + //line sql.y:959 { yyVAL.tableExprs = append(yyVAL.tableExprs, yyDollar[3].tableExpr) } case 156: yyDollar = yyS[yypt-1 : yypt+1] - //line sql.y:970 + //line sql.y:969 { yyVAL.tableExpr = yyDollar[1].aliasedTableName } case 157: yyDollar = yyS[yypt-1 : yypt+1] - //line sql.y:974 + //line sql.y:973 { yyVAL.tableExpr = &AliasedTableExpr{Expr: yyDollar[1].subquery} } case 158: yyDollar = yyS[yypt-3 : yypt+1] - //line sql.y:978 + //line sql.y:977 { yyVAL.tableExpr = &AliasedTableExpr{Expr: yyDollar[1].subquery, As: yyDollar[3].tableIdent} } case 159: yyDollar = yyS[yypt-3 : yypt+1] - //line sql.y:982 + //line sql.y:981 { yyVAL.tableExpr = &ParenTableExpr{Exprs: yyDollar[2].tableExprs} } case 160: yyDollar = yyS[yypt-2 : yypt+1] - //line sql.y:988 + //line sql.y:987 { yyVAL.aliasedTableName = &AliasedTableExpr{Expr: yyDollar[1].tableName, As: yyDollar[2].tableIdent} } case 161: yyDollar = yyS[yypt-1 : yypt+1] - //line sql.y:994 + //line sql.y:993 { yyVAL.columns = Columns{yyDollar[1].colIdent} } case 162: yyDollar = yyS[yypt-3 : yypt+1] - //line sql.y:998 + //line sql.y:997 { yyVAL.columns = append(yyVAL.columns, yyDollar[3].colIdent) } case 163: yyDollar = yyS[yypt-4 : yypt+1] - //line sql.y:1004 + //line sql.y:1003 { yyVAL.tableExpr = &JoinTableExpr{LeftExpr: yyDollar[1].tableExpr, Join: yyDollar[2].str, RightExpr: yyDollar[3].tableExpr, Condition: yyDollar[4].joinCondition} } case 164: yyDollar = yyS[yypt-4 : yypt+1] - //line sql.y:1008 + //line sql.y:1007 { yyVAL.tableExpr = &JoinTableExpr{LeftExpr: yyDollar[1].tableExpr, Join: yyDollar[2].str, RightExpr: yyDollar[3].tableExpr, Condition: yyDollar[4].joinCondition} } case 165: yyDollar = yyS[yypt-3 : yypt+1] - //line sql.y:1012 + //line sql.y:1011 { yyVAL.tableExpr = &JoinTableExpr{LeftExpr: yyDollar[1].tableExpr, Join: yyDollar[2].str, RightExpr: yyDollar[3].tableExpr} } case 166: yyDollar = yyS[yypt-2 : yypt+1] - //line sql.y:1018 + //line sql.y:1017 { yyVAL.joinCondition = JoinCondition{On: yyDollar[2].expr} } case 167: yyDollar = yyS[yypt-4 : yypt+1] - //line sql.y:1020 + //line sql.y:1019 { yyVAL.joinCondition = JoinCondition{Using: yyDollar[3].columns} } case 168: yyDollar = yyS[yypt-0 : yypt+1] - //line sql.y:1024 + //line sql.y:1023 { yyVAL.joinCondition = JoinCondition{} } case 169: yyDollar = yyS[yypt-1 : yypt+1] - //line sql.y:1026 + //line sql.y:1025 { yyVAL.joinCondition = yyDollar[1].joinCondition } case 170: yyDollar = yyS[yypt-0 : yypt+1] - //line sql.y:1029 + //line sql.y:1028 { yyVAL.empty = struct{}{} } case 171: yyDollar = yyS[yypt-1 : yypt+1] - //line sql.y:1031 + //line sql.y:1030 { yyVAL.empty = struct{}{} } case 172: yyDollar = yyS[yypt-0 : yypt+1] - //line sql.y:1034 + //line sql.y:1033 { yyVAL.tableIdent = NewTableIdent("") } case 173: yyDollar = yyS[yypt-1 : yypt+1] - //line sql.y:1038 + //line sql.y:1037 { yyVAL.tableIdent = yyDollar[1].tableIdent } case 174: yyDollar = yyS[yypt-2 : yypt+1] - //line sql.y:1042 + //line sql.y:1041 { yyVAL.tableIdent = yyDollar[2].tableIdent } case 176: yyDollar = yyS[yypt-1 : yypt+1] - //line sql.y:1049 + //line sql.y:1048 { yyVAL.tableIdent = NewTableIdent(string(yyDollar[1].bytes)) } case 177: yyDollar = yyS[yypt-1 : yypt+1] - //line sql.y:1055 + //line sql.y:1054 { yyVAL.str = JoinStr } case 178: yyDollar = yyS[yypt-2 : yypt+1] - //line sql.y:1059 + //line sql.y:1058 { yyVAL.str = InnerJoinStr } case 179: yyDollar = yyS[yypt-2 : yypt+1] - //line sql.y:1063 + //line sql.y:1062 { yyVAL.str = CrossJoinStr } case 180: yyDollar = yyS[yypt-2 : yypt+1] - //line sql.y:1069 + //line sql.y:1068 { yyVAL.str = LeftJoinStr } case 181: yyDollar = yyS[yypt-3 : yypt+1] - //line sql.y:1073 + //line sql.y:1072 { yyVAL.str = LeftJoinStr } case 182: yyDollar = yyS[yypt-2 : yypt+1] - //line sql.y:1079 + //line sql.y:1078 { yyVAL.str = NaturalJoinStr } case 183: yyDollar = yyS[yypt-2 : yypt+1] - //line sql.y:1083 + //line sql.y:1082 { yyVAL.str = NaturalLeftJoinStr } case 184: yyDollar = yyS[yypt-2 : yypt+1] - //line sql.y:1089 + //line sql.y:1088 { yyVAL.tableName = yyDollar[2].tableName } case 185: yyDollar = yyS[yypt-1 : yypt+1] - //line sql.y:1093 + //line sql.y:1092 { yyVAL.tableName = yyDollar[1].tableName } case 186: yyDollar = yyS[yypt-1 : yypt+1] - //line sql.y:1099 + //line sql.y:1098 { yyVAL.tableName = TableName{Name: yyDollar[1].tableIdent} } case 187: yyDollar = yyS[yypt-3 : yypt+1] - //line sql.y:1103 + //line sql.y:1102 { yyVAL.tableName = TableName{Qualifier: yyDollar[1].tableIdent, Name: yyDollar[3].tableIdent} } case 188: yyDollar = yyS[yypt-0 : yypt+1] - //line sql.y:1108 + //line sql.y:1107 { yyVAL.expr = nil } case 189: yyDollar = yyS[yypt-2 : yypt+1] - //line sql.y:1112 + //line sql.y:1111 { yyVAL.expr = yyDollar[2].expr } case 190: yyDollar = yyS[yypt-1 : yypt+1] - //line sql.y:1118 + //line sql.y:1117 { yyVAL.expr = yyDollar[1].expr } case 191: yyDollar = yyS[yypt-3 : yypt+1] - //line sql.y:1122 + //line sql.y:1121 { yyVAL.expr = &AndExpr{Left: yyDollar[1].expr, Right: yyDollar[3].expr} } case 192: yyDollar = yyS[yypt-3 : yypt+1] - //line sql.y:1126 + //line sql.y:1125 { yyVAL.expr = &OrExpr{Left: yyDollar[1].expr, Right: yyDollar[3].expr} } case 193: yyDollar = yyS[yypt-2 : yypt+1] - //line sql.y:1130 + //line sql.y:1129 { yyVAL.expr = &NotExpr{Expr: yyDollar[2].expr} } case 194: yyDollar = yyS[yypt-3 : yypt+1] - //line sql.y:1134 + //line sql.y:1133 { yyVAL.expr = &IsExpr{Operator: yyDollar[3].str, Expr: yyDollar[1].expr} } case 195: yyDollar = yyS[yypt-1 : yypt+1] - //line sql.y:1138 + //line sql.y:1137 { yyVAL.expr = yyDollar[1].expr } case 196: yyDollar = yyS[yypt-2 : yypt+1] - //line sql.y:1142 + //line sql.y:1141 { yyVAL.expr = &Default{ColName: yyDollar[2].str} } case 197: yyDollar = yyS[yypt-0 : yypt+1] - //line sql.y:1148 + //line sql.y:1147 { yyVAL.str = "" } case 198: yyDollar = yyS[yypt-3 : yypt+1] - //line sql.y:1152 + //line sql.y:1151 { yyVAL.str = string(yyDollar[2].bytes) } case 199: yyDollar = yyS[yypt-1 : yypt+1] - //line sql.y:1158 + //line sql.y:1157 { yyVAL.boolVal = BoolVal(true) } case 200: yyDollar = yyS[yypt-1 : yypt+1] - //line sql.y:1162 + //line sql.y:1161 { yyVAL.boolVal = BoolVal(false) } case 201: yyDollar = yyS[yypt-3 : yypt+1] - //line sql.y:1168 + //line sql.y:1167 { yyVAL.expr = &ComparisonExpr{Left: yyDollar[1].expr, Operator: yyDollar[2].str, Right: yyDollar[3].expr} } case 202: yyDollar = yyS[yypt-3 : yypt+1] - //line sql.y:1172 + //line sql.y:1171 { yyVAL.expr = &ComparisonExpr{Left: yyDollar[1].expr, Operator: InStr, Right: yyDollar[3].colTuple} } case 203: yyDollar = yyS[yypt-4 : yypt+1] - //line sql.y:1176 + //line sql.y:1175 { yyVAL.expr = &ComparisonExpr{Left: yyDollar[1].expr, Operator: NotInStr, Right: yyDollar[4].colTuple} } case 204: yyDollar = yyS[yypt-4 : yypt+1] - //line sql.y:1180 + //line sql.y:1179 { yyVAL.expr = &ComparisonExpr{Left: yyDollar[1].expr, Operator: LikeStr, Right: yyDollar[3].expr, Escape: yyDollar[4].expr} } case 205: yyDollar = yyS[yypt-5 : yypt+1] - //line sql.y:1184 + //line sql.y:1183 { yyVAL.expr = &ComparisonExpr{Left: yyDollar[1].expr, Operator: NotLikeStr, Right: yyDollar[4].expr, Escape: yyDollar[5].expr} } case 206: yyDollar = yyS[yypt-3 : yypt+1] - //line sql.y:1188 + //line sql.y:1187 { yyVAL.expr = &ComparisonExpr{Left: yyDollar[1].expr, Operator: RegexpStr, Right: yyDollar[3].expr} } case 207: yyDollar = yyS[yypt-4 : yypt+1] - //line sql.y:1192 + //line sql.y:1191 { yyVAL.expr = &ComparisonExpr{Left: yyDollar[1].expr, Operator: NotRegexpStr, Right: yyDollar[4].expr} } case 208: yyDollar = yyS[yypt-5 : yypt+1] - //line sql.y:1196 + //line sql.y:1195 { yyVAL.expr = &RangeCond{Left: yyDollar[1].expr, Operator: BetweenStr, From: yyDollar[3].expr, To: yyDollar[5].expr} } case 209: yyDollar = yyS[yypt-6 : yypt+1] - //line sql.y:1200 + //line sql.y:1199 { yyVAL.expr = &RangeCond{Left: yyDollar[1].expr, Operator: NotBetweenStr, From: yyDollar[4].expr, To: yyDollar[6].expr} } case 210: yyDollar = yyS[yypt-2 : yypt+1] - //line sql.y:1204 + //line sql.y:1203 { yyVAL.expr = &ExistsExpr{Subquery: yyDollar[2].subquery} } case 211: yyDollar = yyS[yypt-1 : yypt+1] - //line sql.y:1210 + //line sql.y:1209 { yyVAL.str = IsNullStr } case 212: yyDollar = yyS[yypt-2 : yypt+1] - //line sql.y:1214 + //line sql.y:1213 { yyVAL.str = IsNotNullStr } case 213: yyDollar = yyS[yypt-1 : yypt+1] - //line sql.y:1218 + //line sql.y:1217 { yyVAL.str = IsTrueStr } case 214: yyDollar = yyS[yypt-2 : yypt+1] - //line sql.y:1222 + //line sql.y:1221 { yyVAL.str = IsNotTrueStr } case 215: yyDollar = yyS[yypt-1 : yypt+1] - //line sql.y:1226 + //line sql.y:1225 { yyVAL.str = IsFalseStr } case 216: yyDollar = yyS[yypt-2 : yypt+1] - //line sql.y:1230 + //line sql.y:1229 { yyVAL.str = IsNotFalseStr } case 217: yyDollar = yyS[yypt-1 : yypt+1] - //line sql.y:1236 + //line sql.y:1235 { yyVAL.str = EqualStr } case 218: yyDollar = yyS[yypt-1 : yypt+1] - //line sql.y:1240 + //line sql.y:1239 { yyVAL.str = LessThanStr } case 219: yyDollar = yyS[yypt-1 : yypt+1] - //line sql.y:1244 + //line sql.y:1243 { yyVAL.str = GreaterThanStr } case 220: yyDollar = yyS[yypt-1 : yypt+1] - //line sql.y:1248 + //line sql.y:1247 { yyVAL.str = LessEqualStr } case 221: yyDollar = yyS[yypt-1 : yypt+1] - //line sql.y:1252 + //line sql.y:1251 { yyVAL.str = GreaterEqualStr } case 222: yyDollar = yyS[yypt-1 : yypt+1] - //line sql.y:1256 + //line sql.y:1255 { yyVAL.str = NotEqualStr } case 223: yyDollar = yyS[yypt-1 : yypt+1] - //line sql.y:1260 + //line sql.y:1259 { yyVAL.str = NullSafeNotEqualStr } case 224: yyDollar = yyS[yypt-0 : yypt+1] - //line sql.y:1265 + //line sql.y:1264 { yyVAL.expr = nil } case 225: yyDollar = yyS[yypt-2 : yypt+1] - //line sql.y:1269 + //line sql.y:1268 { yyVAL.expr = yyDollar[2].expr } case 226: yyDollar = yyS[yypt-1 : yypt+1] - //line sql.y:1275 + //line sql.y:1274 { yyVAL.colTuple = yyDollar[1].valTuple } case 227: yyDollar = yyS[yypt-1 : yypt+1] - //line sql.y:1279 + //line sql.y:1278 { yyVAL.colTuple = yyDollar[1].subquery } case 228: yyDollar = yyS[yypt-1 : yypt+1] - //line sql.y:1283 + //line sql.y:1282 { yyVAL.colTuple = ListArg(yyDollar[1].bytes) } case 229: yyDollar = yyS[yypt-3 : yypt+1] - //line sql.y:1289 + //line sql.y:1288 { yyVAL.subquery = &Subquery{yyDollar[2].selStmt} } case 230: yyDollar = yyS[yypt-1 : yypt+1] - //line sql.y:1295 + //line sql.y:1294 { yyVAL.exprs = Exprs{yyDollar[1].expr} } case 231: yyDollar = yyS[yypt-3 : yypt+1] - //line sql.y:1299 + //line sql.y:1298 { yyVAL.exprs = append(yyDollar[1].exprs, yyDollar[3].expr) } case 232: yyDollar = yyS[yypt-1 : yypt+1] - //line sql.y:1305 + //line sql.y:1304 { yyVAL.expr = yyDollar[1].expr } case 233: yyDollar = yyS[yypt-1 : yypt+1] - //line sql.y:1309 + //line sql.y:1308 { yyVAL.expr = yyDollar[1].boolVal } case 234: yyDollar = yyS[yypt-1 : yypt+1] - //line sql.y:1313 + //line sql.y:1312 { yyVAL.expr = yyDollar[1].colName } case 235: yyDollar = yyS[yypt-1 : yypt+1] - //line sql.y:1317 + //line sql.y:1316 { yyVAL.expr = yyDollar[1].expr } case 236: yyDollar = yyS[yypt-1 : yypt+1] - //line sql.y:1321 + //line sql.y:1320 { yyVAL.expr = yyDollar[1].subquery } case 237: yyDollar = yyS[yypt-3 : yypt+1] - //line sql.y:1325 + //line sql.y:1324 { yyVAL.expr = &BinaryExpr{Left: yyDollar[1].expr, Operator: BitAndStr, Right: yyDollar[3].expr} } case 238: yyDollar = yyS[yypt-3 : yypt+1] - //line sql.y:1329 + //line sql.y:1328 { yyVAL.expr = &BinaryExpr{Left: yyDollar[1].expr, Operator: BitOrStr, Right: yyDollar[3].expr} } case 239: yyDollar = yyS[yypt-3 : yypt+1] - //line sql.y:1333 + //line sql.y:1332 { yyVAL.expr = &BinaryExpr{Left: yyDollar[1].expr, Operator: BitXorStr, Right: yyDollar[3].expr} } case 240: yyDollar = yyS[yypt-3 : yypt+1] - //line sql.y:1337 + //line sql.y:1336 { yyVAL.expr = &BinaryExpr{Left: yyDollar[1].expr, Operator: PlusStr, Right: yyDollar[3].expr} } case 241: yyDollar = yyS[yypt-3 : yypt+1] - //line sql.y:1341 + //line sql.y:1340 { yyVAL.expr = &BinaryExpr{Left: yyDollar[1].expr, Operator: MinusStr, Right: yyDollar[3].expr} } case 242: yyDollar = yyS[yypt-3 : yypt+1] - //line sql.y:1345 + //line sql.y:1344 { yyVAL.expr = &BinaryExpr{Left: yyDollar[1].expr, Operator: MultStr, Right: yyDollar[3].expr} } case 243: yyDollar = yyS[yypt-3 : yypt+1] - //line sql.y:1349 + //line sql.y:1348 { yyVAL.expr = &BinaryExpr{Left: yyDollar[1].expr, Operator: DivStr, Right: yyDollar[3].expr} } case 244: yyDollar = yyS[yypt-3 : yypt+1] - //line sql.y:1353 + //line sql.y:1352 { yyVAL.expr = &BinaryExpr{Left: yyDollar[1].expr, Operator: IntDivStr, Right: yyDollar[3].expr} } case 245: yyDollar = yyS[yypt-3 : yypt+1] - //line sql.y:1357 + //line sql.y:1356 { yyVAL.expr = &BinaryExpr{Left: yyDollar[1].expr, Operator: ModStr, Right: yyDollar[3].expr} } case 246: yyDollar = yyS[yypt-3 : yypt+1] - //line sql.y:1361 + //line sql.y:1360 { yyVAL.expr = &BinaryExpr{Left: yyDollar[1].expr, Operator: ModStr, Right: yyDollar[3].expr} } case 247: yyDollar = yyS[yypt-3 : yypt+1] - //line sql.y:1365 + //line sql.y:1364 { yyVAL.expr = &BinaryExpr{Left: yyDollar[1].expr, Operator: ShiftLeftStr, Right: yyDollar[3].expr} } case 248: yyDollar = yyS[yypt-3 : yypt+1] - //line sql.y:1369 + //line sql.y:1368 { yyVAL.expr = &BinaryExpr{Left: yyDollar[1].expr, Operator: ShiftRightStr, Right: yyDollar[3].expr} } case 249: yyDollar = yyS[yypt-2 : yypt+1] - //line sql.y:1373 + //line sql.y:1372 { if num, ok := yyDollar[2].expr.(*SQLVal); ok && num.Type == IntVal { yyVAL.expr = num @@ -3052,7 +3038,7 @@ yydefault: } case 250: yyDollar = yyS[yypt-2 : yypt+1] - //line sql.y:1381 + //line sql.y:1380 { if num, ok := yyDollar[2].expr.(*SQLVal); ok && num.Type == IntVal { // Handle double negative @@ -3068,19 +3054,19 @@ yydefault: } case 251: yyDollar = yyS[yypt-2 : yypt+1] - //line sql.y:1395 + //line sql.y:1394 { yyVAL.expr = &UnaryExpr{Operator: TildaStr, Expr: yyDollar[2].expr} } case 252: yyDollar = yyS[yypt-2 : yypt+1] - //line sql.y:1399 + //line sql.y:1398 { yyVAL.expr = &UnaryExpr{Operator: BangStr, Expr: yyDollar[2].expr} } case 253: yyDollar = yyS[yypt-3 : yypt+1] - //line sql.y:1403 + //line sql.y:1402 { // This rule prevents the usage of INTERVAL // as a function. If support is needed for that, @@ -3090,503 +3076,491 @@ yydefault: } case 258: yyDollar = yyS[yypt-4 : yypt+1] - //line sql.y:1421 + //line sql.y:1420 { yyVAL.expr = &FuncExpr{Name: yyDollar[1].colIdent, Exprs: yyDollar[3].selectExprs} } case 259: yyDollar = yyS[yypt-5 : yypt+1] - //line sql.y:1425 + //line sql.y:1424 { yyVAL.expr = &FuncExpr{Name: yyDollar[1].colIdent, Distinct: true, Exprs: yyDollar[4].selectExprs} } case 260: yyDollar = yyS[yypt-6 : yypt+1] - //line sql.y:1429 + //line sql.y:1428 { yyVAL.expr = &FuncExpr{Qualifier: yyDollar[1].tableIdent, Name: yyDollar[3].colIdent, Exprs: yyDollar[5].selectExprs} } case 261: yyDollar = yyS[yypt-6 : yypt+1] - //line sql.y:1439 + //line sql.y:1438 { yyVAL.expr = &ConvertExpr{Expr: yyDollar[3].expr, Type: yyDollar[5].convertType} } case 262: - yyDollar = yyS[yypt-6 : yypt+1] - //line sql.y:1443 - { - yyVAL.expr = &SubstrExpr{Name: yyDollar[3].colName, From: yyDollar[5].expr, To: nil} - } - case 263: - yyDollar = yyS[yypt-8 : yypt+1] - //line sql.y:1447 - { - yyVAL.expr = &SubstrExpr{Name: yyDollar[3].colName, From: yyDollar[5].expr, To: yyDollar[7].expr} - } - case 264: yyDollar = yyS[yypt-7 : yypt+1] - //line sql.y:1451 + //line sql.y:1442 { yyVAL.expr = &GroupConcatExpr{Distinct: yyDollar[3].str, Exprs: yyDollar[4].selectExprs, OrderBy: yyDollar[5].orderBy, Separator: yyDollar[6].str} } - case 265: + case 263: yyDollar = yyS[yypt-5 : yypt+1] - //line sql.y:1455 + //line sql.y:1446 { yyVAL.expr = &CaseExpr{Expr: yyDollar[2].expr, Whens: yyDollar[3].whens, Else: yyDollar[4].expr} } - case 266: + case 264: yyDollar = yyS[yypt-4 : yypt+1] - //line sql.y:1459 + //line sql.y:1450 { yyVAL.expr = &ValuesFuncExpr{Name: yyDollar[3].colName} } - case 267: + case 265: yyDollar = yyS[yypt-1 : yypt+1] - //line sql.y:1469 + //line sql.y:1460 { yyVAL.expr = &TimeExpr{Expr: NewColIdent("current_timestamp")} } - case 268: + case 266: yyDollar = yyS[yypt-1 : yypt+1] - //line sql.y:1474 + //line sql.y:1465 { yyVAL.expr = &TimeExpr{Expr: NewColIdent("current_date")} } - case 269: + case 267: yyDollar = yyS[yypt-1 : yypt+1] - //line sql.y:1479 + //line sql.y:1470 { yyVAL.expr = &TimeExpr{Expr: NewColIdent("current_time")} } - case 270: + case 268: yyDollar = yyS[yypt-4 : yypt+1] - //line sql.y:1489 + //line sql.y:1480 { yyVAL.expr = &FuncExpr{Name: NewColIdent("if"), Exprs: yyDollar[3].selectExprs} } - case 271: + case 269: yyDollar = yyS[yypt-4 : yypt+1] - //line sql.y:1493 + //line sql.y:1484 { yyVAL.expr = &FuncExpr{Name: NewColIdent("mod"), Exprs: yyDollar[3].selectExprs} } - case 272: + case 270: yyDollar = yyS[yypt-4 : yypt+1] - //line sql.y:1497 + //line sql.y:1488 { yyVAL.expr = &FuncExpr{Name: NewColIdent("replace"), Exprs: yyDollar[3].selectExprs} } - case 273: + case 271: yyDollar = yyS[yypt-2 : yypt+1] - //line sql.y:1503 + //line sql.y:1494 { yyVAL.convertType = &ConvertType{Type: string(yyDollar[1].bytes), Length: yyDollar[2].optVal} } - case 274: + case 272: yyDollar = yyS[yypt-1 : yypt+1] - //line sql.y:1507 + //line sql.y:1498 { yyVAL.convertType = &ConvertType{Type: string(yyDollar[1].bytes)} } - case 275: + case 273: yyDollar = yyS[yypt-2 : yypt+1] - //line sql.y:1511 + //line sql.y:1502 { yyVAL.convertType = &ConvertType{Type: string(yyDollar[1].bytes), Length: yyDollar[2].optVal} } - case 276: + case 274: yyDollar = yyS[yypt-2 : yypt+1] - //line sql.y:1515 + //line sql.y:1506 { yyVAL.convertType = &ConvertType{Type: string(yyDollar[1].bytes)} yyVAL.convertType.Length = yyDollar[2].LengthScaleOption.Length yyVAL.convertType.Scale = yyDollar[2].LengthScaleOption.Scale } - case 277: + case 275: yyDollar = yyS[yypt-2 : yypt+1] - //line sql.y:1521 + //line sql.y:1512 { yyVAL.convertType = &ConvertType{Type: string(yyDollar[1].bytes), Length: yyDollar[2].optVal} } - case 278: + case 276: yyDollar = yyS[yypt-1 : yypt+1] - //line sql.y:1525 + //line sql.y:1516 { yyVAL.convertType = &ConvertType{Type: string(yyDollar[1].bytes)} } - case 279: + case 277: yyDollar = yyS[yypt-2 : yypt+1] - //line sql.y:1529 + //line sql.y:1520 { yyVAL.convertType = &ConvertType{Type: string(yyDollar[1].bytes)} } - case 280: + case 278: yyDollar = yyS[yypt-2 : yypt+1] - //line sql.y:1533 + //line sql.y:1524 { yyVAL.convertType = &ConvertType{Type: string(yyDollar[1].bytes), Length: yyDollar[2].optVal} } - case 281: + case 279: yyDollar = yyS[yypt-1 : yypt+1] - //line sql.y:1537 + //line sql.y:1528 { yyVAL.convertType = &ConvertType{Type: string(yyDollar[1].bytes)} } - case 282: + case 280: yyDollar = yyS[yypt-2 : yypt+1] - //line sql.y:1541 + //line sql.y:1532 { yyVAL.convertType = &ConvertType{Type: string(yyDollar[1].bytes)} } - case 283: + case 281: yyDollar = yyS[yypt-2 : yypt+1] - //line sql.y:1545 + //line sql.y:1536 { yyVAL.convertType = &ConvertType{Type: string(yyDollar[1].bytes), Length: yyDollar[2].optVal} } - case 284: + case 282: yyDollar = yyS[yypt-2 : yypt+1] - //line sql.y:1549 + //line sql.y:1540 { yyVAL.convertType = &ConvertType{Type: string(yyDollar[1].bytes), Length: yyDollar[2].optVal} } - case 285: + case 283: yyDollar = yyS[yypt-2 : yypt+1] - //line sql.y:1553 + //line sql.y:1544 { yyVAL.convertType = &ConvertType{Type: string(yyDollar[1].bytes), Length: yyDollar[2].optVal} } - case 286: + case 284: yyDollar = yyS[yypt-2 : yypt+1] - //line sql.y:1557 + //line sql.y:1548 { yyVAL.convertType = &ConvertType{Type: string(yyDollar[1].bytes), Length: yyDollar[2].optVal} } - case 287: + case 285: yyDollar = yyS[yypt-2 : yypt+1] - //line sql.y:1561 + //line sql.y:1552 { yyVAL.convertType = &ConvertType{Type: string(yyDollar[1].bytes), Length: yyDollar[2].optVal} } - case 288: + case 286: yyDollar = yyS[yypt-2 : yypt+1] - //line sql.y:1565 + //line sql.y:1556 { yyVAL.convertType = &ConvertType{Type: string(yyDollar[1].bytes), Length: yyDollar[2].optVal} } - case 289: + case 287: yyDollar = yyS[yypt-1 : yypt+1] - //line sql.y:1569 + //line sql.y:1560 { yyVAL.convertType = &ConvertType{Type: string(yyDollar[1].bytes)} } - case 290: + case 288: yyDollar = yyS[yypt-0 : yypt+1] - //line sql.y:1574 + //line sql.y:1565 { yyVAL.expr = nil } - case 291: + case 289: yyDollar = yyS[yypt-1 : yypt+1] - //line sql.y:1578 + //line sql.y:1569 { yyVAL.expr = yyDollar[1].expr } - case 292: + case 290: yyDollar = yyS[yypt-0 : yypt+1] - //line sql.y:1583 + //line sql.y:1574 { yyVAL.str = string("") } - case 293: + case 291: yyDollar = yyS[yypt-2 : yypt+1] - //line sql.y:1587 + //line sql.y:1578 { yyVAL.str = " separator '" + string(yyDollar[2].bytes) + "'" } - case 294: + case 292: yyDollar = yyS[yypt-1 : yypt+1] - //line sql.y:1593 + //line sql.y:1584 { yyVAL.whens = []*When{yyDollar[1].when} } - case 295: + case 293: yyDollar = yyS[yypt-2 : yypt+1] - //line sql.y:1597 + //line sql.y:1588 { yyVAL.whens = append(yyDollar[1].whens, yyDollar[2].when) } - case 296: + case 294: yyDollar = yyS[yypt-4 : yypt+1] - //line sql.y:1603 + //line sql.y:1594 { yyVAL.when = &When{Cond: yyDollar[2].expr, Val: yyDollar[4].expr} } - case 297: + case 295: yyDollar = yyS[yypt-0 : yypt+1] - //line sql.y:1608 + //line sql.y:1599 { yyVAL.expr = nil } - case 298: + case 296: yyDollar = yyS[yypt-2 : yypt+1] - //line sql.y:1612 + //line sql.y:1603 { yyVAL.expr = yyDollar[2].expr } - case 299: + case 297: yyDollar = yyS[yypt-1 : yypt+1] - //line sql.y:1618 + //line sql.y:1609 { yyVAL.colName = &ColName{Name: yyDollar[1].colIdent} } - case 300: + case 298: yyDollar = yyS[yypt-3 : yypt+1] - //line sql.y:1622 + //line sql.y:1613 { yyVAL.colName = &ColName{Qualifier: TableName{Name: yyDollar[1].tableIdent}, Name: yyDollar[3].colIdent} } - case 301: + case 299: yyDollar = yyS[yypt-5 : yypt+1] - //line sql.y:1626 + //line sql.y:1617 { yyVAL.colName = &ColName{Qualifier: TableName{Qualifier: yyDollar[1].tableIdent, Name: yyDollar[3].tableIdent}, Name: yyDollar[5].colIdent} } - case 302: + case 300: yyDollar = yyS[yypt-1 : yypt+1] - //line sql.y:1632 + //line sql.y:1623 { yyVAL.expr = NewStrVal(yyDollar[1].bytes) } - case 303: + case 301: yyDollar = yyS[yypt-1 : yypt+1] - //line sql.y:1636 + //line sql.y:1627 { yyVAL.expr = NewHexVal(yyDollar[1].bytes) } - case 304: + case 302: yyDollar = yyS[yypt-1 : yypt+1] - //line sql.y:1640 + //line sql.y:1631 { yyVAL.expr = NewIntVal(yyDollar[1].bytes) } - case 305: + case 303: yyDollar = yyS[yypt-1 : yypt+1] - //line sql.y:1644 + //line sql.y:1635 { yyVAL.expr = NewFloatVal(yyDollar[1].bytes) } - case 306: + case 304: yyDollar = yyS[yypt-1 : yypt+1] - //line sql.y:1648 + //line sql.y:1639 { yyVAL.expr = NewHexNum(yyDollar[1].bytes) } - case 307: + case 305: yyDollar = yyS[yypt-1 : yypt+1] - //line sql.y:1652 + //line sql.y:1643 { yyVAL.expr = NewValArg(yyDollar[1].bytes) } - case 308: + case 306: yyDollar = yyS[yypt-1 : yypt+1] - //line sql.y:1656 + //line sql.y:1647 { yyVAL.expr = &NullVal{} } - case 309: + case 307: yyDollar = yyS[yypt-0 : yypt+1] - //line sql.y:1661 + //line sql.y:1652 { yyVAL.exprs = nil } - case 310: + case 308: yyDollar = yyS[yypt-3 : yypt+1] - //line sql.y:1665 + //line sql.y:1656 { yyVAL.exprs = yyDollar[3].exprs } - case 311: + case 309: yyDollar = yyS[yypt-0 : yypt+1] - //line sql.y:1670 + //line sql.y:1661 { yyVAL.expr = nil } - case 312: + case 310: yyDollar = yyS[yypt-2 : yypt+1] - //line sql.y:1674 + //line sql.y:1665 { yyVAL.expr = yyDollar[2].expr } - case 313: + case 311: yyDollar = yyS[yypt-0 : yypt+1] - //line sql.y:1679 + //line sql.y:1670 { yyVAL.orderBy = nil } - case 314: + case 312: yyDollar = yyS[yypt-3 : yypt+1] - //line sql.y:1683 + //line sql.y:1674 { yyVAL.orderBy = yyDollar[3].orderBy } - case 315: + case 313: yyDollar = yyS[yypt-1 : yypt+1] - //line sql.y:1689 + //line sql.y:1680 { yyVAL.orderBy = OrderBy{yyDollar[1].order} } - case 316: + case 314: yyDollar = yyS[yypt-3 : yypt+1] - //line sql.y:1693 + //line sql.y:1684 { yyVAL.orderBy = append(yyDollar[1].orderBy, yyDollar[3].order) } - case 317: + case 315: yyDollar = yyS[yypt-2 : yypt+1] - //line sql.y:1699 + //line sql.y:1690 { yyVAL.order = &Order{Expr: yyDollar[1].expr, Direction: yyDollar[2].str} } - case 318: + case 316: yyDollar = yyS[yypt-0 : yypt+1] - //line sql.y:1704 + //line sql.y:1695 { yyVAL.str = AscScr } - case 319: + case 317: yyDollar = yyS[yypt-1 : yypt+1] - //line sql.y:1708 + //line sql.y:1699 { yyVAL.str = AscScr } - case 320: + case 318: yyDollar = yyS[yypt-1 : yypt+1] - //line sql.y:1712 + //line sql.y:1703 { yyVAL.str = DescScr } - case 321: + case 319: yyDollar = yyS[yypt-0 : yypt+1] - //line sql.y:1717 + //line sql.y:1708 { yyVAL.limit = nil } - case 322: + case 320: yyDollar = yyS[yypt-2 : yypt+1] - //line sql.y:1721 + //line sql.y:1712 { yyVAL.limit = &Limit{Rowcount: yyDollar[2].expr} } - case 323: + case 321: yyDollar = yyS[yypt-4 : yypt+1] - //line sql.y:1725 + //line sql.y:1716 { yyVAL.limit = &Limit{Offset: yyDollar[2].expr, Rowcount: yyDollar[4].expr} } - case 324: + case 322: yyDollar = yyS[yypt-4 : yypt+1] - //line sql.y:1729 + //line sql.y:1720 { yyVAL.limit = &Limit{Offset: yyDollar[4].expr, Rowcount: yyDollar[2].expr} } - case 325: + case 323: yyDollar = yyS[yypt-2 : yypt+1] - //line sql.y:1742 + //line sql.y:1733 { yyVAL.ins = &Insert{Rows: yyDollar[2].values} } - case 326: + case 324: yyDollar = yyS[yypt-1 : yypt+1] - //line sql.y:1746 + //line sql.y:1737 { yyVAL.ins = &Insert{Rows: yyDollar[1].selStmt} } - case 327: + case 325: yyDollar = yyS[yypt-3 : yypt+1] - //line sql.y:1750 + //line sql.y:1741 { // Drop the redundant parenthesis. yyVAL.ins = &Insert{Rows: yyDollar[2].selStmt} } - case 328: + case 326: yyDollar = yyS[yypt-5 : yypt+1] - //line sql.y:1755 + //line sql.y:1746 { yyVAL.ins = &Insert{Columns: yyDollar[2].columns, Rows: yyDollar[5].values} } - case 329: + case 327: yyDollar = yyS[yypt-4 : yypt+1] - //line sql.y:1759 + //line sql.y:1750 { yyVAL.ins = &Insert{Columns: yyDollar[2].columns, Rows: yyDollar[4].selStmt} } - case 330: + case 328: yyDollar = yyS[yypt-6 : yypt+1] - //line sql.y:1763 + //line sql.y:1754 { // Drop the redundant parenthesis. yyVAL.ins = &Insert{Columns: yyDollar[2].columns, Rows: yyDollar[5].selStmt} } - case 331: + case 329: yyDollar = yyS[yypt-1 : yypt+1] - //line sql.y:1770 + //line sql.y:1761 { yyVAL.columns = Columns{yyDollar[1].colIdent} } - case 332: + case 330: yyDollar = yyS[yypt-3 : yypt+1] - //line sql.y:1774 + //line sql.y:1765 { yyVAL.columns = Columns{yyDollar[3].colIdent} } - case 333: + case 331: yyDollar = yyS[yypt-3 : yypt+1] - //line sql.y:1778 + //line sql.y:1769 { yyVAL.columns = append(yyVAL.columns, yyDollar[3].colIdent) } - case 334: + case 332: yyDollar = yyS[yypt-5 : yypt+1] - //line sql.y:1782 + //line sql.y:1773 { yyVAL.columns = append(yyVAL.columns, yyDollar[5].colIdent) } - case 335: + case 333: yyDollar = yyS[yypt-1 : yypt+1] - //line sql.y:1788 + //line sql.y:1779 { yyVAL.values = Values{yyDollar[1].valTuple} } - case 336: + case 334: yyDollar = yyS[yypt-3 : yypt+1] - //line sql.y:1792 + //line sql.y:1783 { yyVAL.values = append(yyDollar[1].values, yyDollar[3].valTuple) } - case 337: + case 335: yyDollar = yyS[yypt-1 : yypt+1] - //line sql.y:1798 + //line sql.y:1789 { yyVAL.valTuple = yyDollar[1].valTuple } - case 338: + case 336: yyDollar = yyS[yypt-2 : yypt+1] - //line sql.y:1802 + //line sql.y:1793 { yyVAL.valTuple = ValTuple{} } - case 339: + case 337: yyDollar = yyS[yypt-3 : yypt+1] - //line sql.y:1808 + //line sql.y:1799 { yyVAL.valTuple = ValTuple(yyDollar[2].exprs) } - case 340: + case 338: yyDollar = yyS[yypt-1 : yypt+1] - //line sql.y:1814 + //line sql.y:1805 { if len(yyDollar[1].valTuple) == 1 { yyVAL.expr = &ParenExpr{yyDollar[1].valTuple[0]} @@ -3594,150 +3568,150 @@ yydefault: yyVAL.expr = yyDollar[1].valTuple } } - case 341: + case 339: yyDollar = yyS[yypt-1 : yypt+1] - //line sql.y:1824 + //line sql.y:1815 { yyVAL.updateExprs = UpdateExprs{yyDollar[1].updateExpr} } - case 342: + case 340: yyDollar = yyS[yypt-3 : yypt+1] - //line sql.y:1828 + //line sql.y:1819 { yyVAL.updateExprs = append(yyDollar[1].updateExprs, yyDollar[3].updateExpr) } - case 343: + case 341: yyDollar = yyS[yypt-3 : yypt+1] - //line sql.y:1834 + //line sql.y:1825 { yyVAL.updateExpr = &UpdateExpr{Name: yyDollar[1].colName, Expr: yyDollar[3].expr} } - case 344: + case 342: yyDollar = yyS[yypt-0 : yypt+1] - //line sql.y:1839 + //line sql.y:1830 { yyVAL.byt = 0 } - case 345: + case 343: yyDollar = yyS[yypt-2 : yypt+1] - //line sql.y:1841 + //line sql.y:1832 { yyVAL.byt = 1 } - case 346: + case 344: yyDollar = yyS[yypt-0 : yypt+1] - //line sql.y:1844 + //line sql.y:1835 { yyVAL.empty = struct{}{} } - case 347: + case 345: yyDollar = yyS[yypt-3 : yypt+1] - //line sql.y:1846 + //line sql.y:1837 { yyVAL.empty = struct{}{} } - case 348: + case 346: yyDollar = yyS[yypt-0 : yypt+1] - //line sql.y:1849 + //line sql.y:1840 { yyVAL.str = "" } - case 349: + case 347: yyDollar = yyS[yypt-1 : yypt+1] - //line sql.y:1851 + //line sql.y:1842 { yyVAL.str = IgnoreStr } - case 350: + case 348: yyDollar = yyS[yypt-0 : yypt+1] - //line sql.y:1854 + //line sql.y:1845 { yyVAL.empty = struct{}{} } - case 351: + case 349: yyDollar = yyS[yypt-1 : yypt+1] - //line sql.y:1856 + //line sql.y:1847 { yyVAL.empty = struct{}{} } - case 352: + case 350: yyDollar = yyS[yypt-1 : yypt+1] - //line sql.y:1858 + //line sql.y:1849 { yyVAL.empty = struct{}{} } - case 353: + case 351: yyDollar = yyS[yypt-1 : yypt+1] - //line sql.y:1862 + //line sql.y:1853 { yyVAL.colIdent = NewColIdent(string(yyDollar[1].bytes)) } - case 354: + case 352: yyDollar = yyS[yypt-1 : yypt+1] - //line sql.y:1866 + //line sql.y:1857 { yyVAL.colIdent = NewColIdent(string(yyDollar[1].bytes)) } - case 356: + case 354: yyDollar = yyS[yypt-1 : yypt+1] - //line sql.y:1873 + //line sql.y:1864 { yyVAL.colIdent = NewColIdent(string(yyDollar[1].bytes)) } - case 357: + case 355: yyDollar = yyS[yypt-1 : yypt+1] - //line sql.y:1879 + //line sql.y:1870 { yyVAL.tableIdent = NewTableIdent(string(yyDollar[1].bytes)) } - case 358: + case 356: yyDollar = yyS[yypt-1 : yypt+1] - //line sql.y:1883 + //line sql.y:1874 { yyVAL.tableIdent = NewTableIdent(string(yyDollar[1].bytes)) } - case 360: + case 358: yyDollar = yyS[yypt-1 : yypt+1] - //line sql.y:1890 + //line sql.y:1881 { yyVAL.tableIdent = NewTableIdent(string(yyDollar[1].bytes)) } - case 469: + case 466: yyDollar = yyS[yypt-1 : yypt+1] - //line sql.y:2024 + //line sql.y:2014 { if incNesting(yylex) { yylex.Error("max nesting level reached") return 1 } } - case 470: + case 467: yyDollar = yyS[yypt-1 : yypt+1] - //line sql.y:2033 + //line sql.y:2023 { decNesting(yylex) } - case 471: + case 468: yyDollar = yyS[yypt-0 : yypt+1] - //line sql.y:2038 + //line sql.y:2028 { forceEOF(yylex) } - case 472: + case 469: yyDollar = yyS[yypt-0 : yypt+1] - //line sql.y:2043 + //line sql.y:2033 { forceEOF(yylex) } - case 473: + case 470: yyDollar = yyS[yypt-1 : yypt+1] - //line sql.y:2047 + //line sql.y:2037 { forceEOF(yylex) } - case 474: + case 471: yyDollar = yyS[yypt-1 : yypt+1] - //line sql.y:2051 + //line sql.y:2041 { forceEOF(yylex) } diff --git a/vendor/github.com/CovenantSQL/sqlparser/sql.y b/vendor/github.com/CovenantSQL/sqlparser/sql.y index 52c795e3c..ca86c1b74 100644 --- a/vendor/github.com/CovenantSQL/sqlparser/sql.y +++ b/vendor/github.com/CovenantSQL/sqlparser/sql.y @@ -156,7 +156,6 @@ func forceEOF(yylex interface{}) { %token CURRENT_TIMESTAMP CURRENT_DATE CURRENT_TIME %token REPLACE %token CAST -%token SUBSTR %token GROUP_CONCAT SEPARATOR // MySQL reserved words that are unused by this grammar will map to this token. @@ -1439,14 +1438,6 @@ function_call_keyword: { $$ = &ConvertExpr{Expr: $3, Type: $5} } -| SUBSTR openb column_name ',' value_expression closeb - { - $$ = &SubstrExpr{Name: $3, From: $5, To: nil} - } -| SUBSTR openb column_name ',' value_expression ',' value_expression closeb - { - $$ = &SubstrExpr{Name: $3, From: $5, To: $7} - } | GROUP_CONCAT openb distinct_opt select_expression_list order_by_opt separator_opt closeb { $$ = &GroupConcatExpr{Distinct: $3, Exprs: $4, OrderBy: $5, Separator: $6} @@ -1914,7 +1905,6 @@ reserved_keyword: | CURRENT_DATE | CURRENT_TIME | CURRENT_TIMESTAMP -| SUBSTR | DEFAULT | DELETE | DESC diff --git a/vendor/github.com/CovenantSQL/sqlparser/token.go b/vendor/github.com/CovenantSQL/sqlparser/token.go index f82d747c9..d357003da 100644 --- a/vendor/github.com/CovenantSQL/sqlparser/token.go +++ b/vendor/github.com/CovenantSQL/sqlparser/token.go @@ -112,7 +112,6 @@ var keywords = map[string]int{ "condition": UNUSED, "constraint": CONSTRAINT, "continue": UNUSED, - "substr": SUBSTR, "create": CREATE, "cross": CROSS, "current_date": CURRENT_DATE, diff --git a/vendor/github.com/btcsuite/btcd/btcec/signature.go b/vendor/github.com/btcsuite/btcd/btcec/signature.go index a4781b00f..bddb22831 100644 --- a/vendor/github.com/btcsuite/btcd/btcec/signature.go +++ b/vendor/github.com/btcsuite/btcd/btcec/signature.go @@ -427,9 +427,7 @@ func signRFC6979(privateKey *PrivateKey, hash []byte) (*Signature, error) { k := nonceRFC6979(privkey.D, hash) inv := new(big.Int).ModInverse(k, N) r, _ := privkey.Curve.ScalarBaseMult(k.Bytes()) - if r.Cmp(N) == 1 { - r.Sub(r, N) - } + r.Mod(r, N) if r.Sign() == 0 { return nil, errors.New("calculated R is zero") diff --git a/vendor/github.com/prometheus/client_golang/prometheus/desc.go b/vendor/github.com/prometheus/client_golang/prometheus/desc.go index 7b8827ffb..1d034f871 100644 --- a/vendor/github.com/prometheus/client_golang/prometheus/desc.go +++ b/vendor/github.com/prometheus/client_golang/prometheus/desc.go @@ -93,7 +93,7 @@ func NewDesc(fqName, help string, variableLabels []string, constLabels Labels) * // First add only the const label names and sort them... for labelName := range constLabels { if !checkLabelName(labelName) { - d.err = fmt.Errorf("%q is not a valid label name", labelName) + d.err = fmt.Errorf("%q is not a valid label name for metric %q", labelName, fqName) return d } labelNames = append(labelNames, labelName) @@ -115,7 +115,7 @@ func NewDesc(fqName, help string, variableLabels []string, constLabels Labels) * // dimension with a different mix between preset and variable labels. for _, labelName := range variableLabels { if !checkLabelName(labelName) { - d.err = fmt.Errorf("%q is not a valid label name", labelName) + d.err = fmt.Errorf("%q is not a valid label name for metric %q", labelName, fqName) return d } labelNames = append(labelNames, "$"+labelName) diff --git a/vendor/github.com/syndtr/goleveldb/leveldb/opt/options.go b/vendor/github.com/syndtr/goleveldb/leveldb/opt/options.go index 44e7d9adc..528b16423 100644 --- a/vendor/github.com/syndtr/goleveldb/leveldb/opt/options.go +++ b/vendor/github.com/syndtr/goleveldb/leveldb/opt/options.go @@ -158,6 +158,12 @@ type Options struct { // The default value is 8MiB. BlockCacheCapacity int + // BlockCacheEvictRemoved allows enable forced-eviction on cached block belonging + // to removed 'sorted table'. + // + // The default if false. + BlockCacheEvictRemoved bool + // BlockRestartInterval is the number of keys between restart points for // delta encoding of keys. // @@ -384,6 +390,13 @@ func (o *Options) GetBlockCacheCapacity() int { return o.BlockCacheCapacity } +func (o *Options) GetBlockCacheEvictRemoved() bool { + if o == nil { + return false + } + return o.BlockCacheEvictRemoved +} + func (o *Options) GetBlockRestartInterval() int { if o == nil || o.BlockRestartInterval <= 0 { return DefaultBlockRestartInterval diff --git a/vendor/github.com/syndtr/goleveldb/leveldb/table.go b/vendor/github.com/syndtr/goleveldb/leveldb/table.go index 87f5a9319..1fac60d05 100644 --- a/vendor/github.com/syndtr/goleveldb/leveldb/table.go +++ b/vendor/github.com/syndtr/goleveldb/leveldb/table.go @@ -290,11 +290,12 @@ func (x *tFilesSortByNum) Less(i, j int) bool { // Table operations. type tOps struct { - s *session - noSync bool - cache *cache.Cache - bcache *cache.Cache - bpool *util.BufferPool + s *session + noSync bool + evictRemoved bool + cache *cache.Cache + bcache *cache.Cache + bpool *util.BufferPool } // Creates an empty table and returns table writer. @@ -422,7 +423,7 @@ func (t *tOps) remove(f *tFile) { } else { t.s.logf("table@remove removed @%d", f.fd.Num) } - if t.bcache != nil { + if t.evictRemoved && t.bcache != nil { t.bcache.EvictNS(uint64(f.fd.Num)) } }) @@ -459,11 +460,12 @@ func newTableOps(s *session) *tOps { bpool = util.NewBufferPool(s.o.GetBlockSize() + 5) } return &tOps{ - s: s, - noSync: s.o.GetNoSync(), - cache: cache.NewCache(cacher), - bcache: bcache, - bpool: bpool, + s: s, + noSync: s.o.GetNoSync(), + evictRemoved: s.o.GetBlockCacheEvictRemoved(), + cache: cache.NewCache(cacher), + bcache: bcache, + bpool: bpool, } } diff --git a/vendor/golang.org/x/crypto/ssh/terminal/util.go b/vendor/golang.org/x/crypto/ssh/terminal/util.go index 731c89a28..391104084 100644 --- a/vendor/golang.org/x/crypto/ssh/terminal/util.go +++ b/vendor/golang.org/x/crypto/ssh/terminal/util.go @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -// +build darwin dragonfly freebsd linux,!appengine netbsd openbsd +// +build aix darwin dragonfly freebsd linux,!appengine netbsd openbsd // Package terminal provides support functions for dealing with terminals, as // commonly found on UNIX systems. @@ -25,7 +25,7 @@ type State struct { termios unix.Termios } -// IsTerminal returns true if the given file descriptor is a terminal. +// IsTerminal returns whether the given file descriptor is a terminal. func IsTerminal(fd int) bool { _, err := unix.IoctlGetTermios(fd, ioctlReadTermios) return err == nil diff --git a/vendor/golang.org/x/crypto/ssh/terminal/util_aix.go b/vendor/golang.org/x/crypto/ssh/terminal/util_aix.go new file mode 100644 index 000000000..dfcd62785 --- /dev/null +++ b/vendor/golang.org/x/crypto/ssh/terminal/util_aix.go @@ -0,0 +1,12 @@ +// Copyright 2018 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. + +// +build aix + +package terminal + +import "golang.org/x/sys/unix" + +const ioctlReadTermios = unix.TCGETS +const ioctlWriteTermios = unix.TCSETS diff --git a/vendor/golang.org/x/crypto/ssh/terminal/util_plan9.go b/vendor/golang.org/x/crypto/ssh/terminal/util_plan9.go index 799f049f0..9317ac7ed 100644 --- a/vendor/golang.org/x/crypto/ssh/terminal/util_plan9.go +++ b/vendor/golang.org/x/crypto/ssh/terminal/util_plan9.go @@ -21,7 +21,7 @@ import ( type State struct{} -// IsTerminal returns true if the given file descriptor is a terminal. +// IsTerminal returns whether the given file descriptor is a terminal. func IsTerminal(fd int) bool { return false } diff --git a/vendor/golang.org/x/crypto/ssh/terminal/util_solaris.go b/vendor/golang.org/x/crypto/ssh/terminal/util_solaris.go index 9e41b9f43..3d5f06a9f 100644 --- a/vendor/golang.org/x/crypto/ssh/terminal/util_solaris.go +++ b/vendor/golang.org/x/crypto/ssh/terminal/util_solaris.go @@ -17,7 +17,7 @@ type State struct { termios unix.Termios } -// IsTerminal returns true if the given file descriptor is a terminal. +// IsTerminal returns whether the given file descriptor is a terminal. func IsTerminal(fd int) bool { _, err := unix.IoctlGetTermio(fd, unix.TCGETA) return err == nil diff --git a/vendor/golang.org/x/crypto/ssh/terminal/util_windows.go b/vendor/golang.org/x/crypto/ssh/terminal/util_windows.go index 8618955df..6cb8a9503 100644 --- a/vendor/golang.org/x/crypto/ssh/terminal/util_windows.go +++ b/vendor/golang.org/x/crypto/ssh/terminal/util_windows.go @@ -26,7 +26,7 @@ type State struct { mode uint32 } -// IsTerminal returns true if the given file descriptor is a terminal. +// IsTerminal returns whether the given file descriptor is a terminal. func IsTerminal(fd int) bool { var st uint32 err := windows.GetConsoleMode(windows.Handle(fd), &st) diff --git a/vendor/golang.org/x/sys/unix/syscall_linux.go b/vendor/golang.org/x/sys/unix/syscall_linux.go index 96ff70dcf..466b2576d 100644 --- a/vendor/golang.org/x/sys/unix/syscall_linux.go +++ b/vendor/golang.org/x/sys/unix/syscall_linux.go @@ -57,6 +57,15 @@ func Fchmodat(dirfd int, path string, mode uint32, flags int) (err error) { // ioctl itself should not be exposed directly, but additional get/set // functions for specific types are permissible. +// IoctlSetPointerInt performs an ioctl operation which sets an +// integer value on fd, using the specified request number. The ioctl +// argument is called with a pointer to the integer value, rather than +// passing the integer value directly. +func IoctlSetPointerInt(fd int, req uint, value int) error { + v := int32(value) + return ioctl(fd, req, uintptr(unsafe.Pointer(&v))) +} + // IoctlSetInt performs an ioctl operation which sets an integer value // on fd, using the specified request number. func IoctlSetInt(fd int, req uint, value int) error { From d396af344b50bee5dc6b46d0742667d1997cb435 Mon Sep 17 00:00:00 2001 From: Qi Xiao Date: Tue, 27 Nov 2018 14:59:04 +0800 Subject: [PATCH 15/24] Complete resolver logic --- cmd/cql-mysql-adapter/resolver/meta.go | 22 +- cmd/cql-mysql-adapter/resolver/query.go | 7 +- cmd/cql-mysql-adapter/resolver/resolver.go | 26 +- cmd/cql-secure-gateway/cursor.go | 2 + cmd/cql-secure-gateway/resolver.go | 464 ++++++++++++++------- cmd/cql-secure-gateway/resolver_test.go | 411 +++++++++++++++++- cmd/cql-secure-gateway/xxx_test.go | 1 + 7 files changed, 754 insertions(+), 179 deletions(-) diff --git a/cmd/cql-mysql-adapter/resolver/meta.go b/cmd/cql-mysql-adapter/resolver/meta.go index 48d5d2474..f2760bd1d 100644 --- a/cmd/cql-mysql-adapter/resolver/meta.go +++ b/cmd/cql-mysql-adapter/resolver/meta.go @@ -28,18 +28,23 @@ import ( ) const ( + // MetaRefreshInterval defines the default database meta re-scan interval. MetaRefreshInterval = time.Minute ) var ( - ErrDBNotExists = errors.New("database not exists") + // ErrDBNotExists defines the no database found meta error. + ErrDBNotExists = errors.New("database not exists") + // ErrTableNotExists defines the no table found meta error. ErrTableNotExists = errors.New("Table not exists") ) +// DBHandler defines registered database connection referenced in meta resolver. type DBHandler interface { Query(query string, args ...interface{}) (rows *sql.Rows, err error) } +// DBMetaHandler defines single database meta resolve handler. type DBMetaHandler struct { l sync.RWMutex dbID string @@ -48,6 +53,7 @@ type DBMetaHandler struct { tableMapping map[string][]string } +// MetaHandler defines unified database meta resolve handler. type MetaHandler struct { l sync.RWMutex dbMap map[string]*DBMetaHandler @@ -56,6 +62,7 @@ type MetaHandler struct { stopOnce sync.Once } +// NewMetaHandler returns a new MetaHandler instance. func NewMetaHandler() (h *MetaHandler) { h = &MetaHandler{ dbMap: make(map[string]*DBMetaHandler), @@ -78,6 +85,7 @@ func (h *MetaHandler) autoReloadMeta() { } } +// Stop stops all running meta refresher. func (h *MetaHandler) Stop() { h.stopOnce.Do(func() { if h.stopCh != nil { @@ -91,6 +99,7 @@ func (h *MetaHandler) Stop() { }) } +// AddConn add new database connection to meta refresher. func (h *MetaHandler) AddConn(dbID string, conn DBHandler) { h.l.RLock() _, exists := h.dbMap[dbID] @@ -100,6 +109,7 @@ func (h *MetaHandler) AddConn(dbID string, conn DBHandler) { } } +// SetConn set new database connection to meta refresher. func (h *MetaHandler) SetConn(dbID string, conn DBHandler) { h.l.Lock() defer h.l.Unlock() @@ -107,6 +117,7 @@ func (h *MetaHandler) SetConn(dbID string, conn DBHandler) { h.dbMap[dbID] = NewDBMetaHandler(dbID, conn) } +// GetConn gets database connection from meta resolver. func (h *MetaHandler) GetConn(dbID string) (conn DBHandler, exists bool) { h.l.RLock() defer h.l.RUnlock() @@ -120,6 +131,7 @@ func (h *MetaHandler) GetConn(dbID string) (conn DBHandler, exists bool) { return } +// GetTables gets tables list from specified database. func (h *MetaHandler) GetTables(dbID string) (tables []string, err error) { h.l.RLock() defer h.l.RUnlock() @@ -133,6 +145,7 @@ func (h *MetaHandler) GetTables(dbID string) (tables []string, err error) { return } +// GetTable gets table column from specified database and table. func (h *MetaHandler) GetTable(dbID string, tableName string) (columns []string, err error) { h.l.RLock() defer h.l.RUnlock() @@ -146,6 +159,7 @@ func (h *MetaHandler) GetTable(dbID string, tableName string) (columns []string, return } +// ReloadMeta manually reload database meta (just a cache clean). func (h *MetaHandler) ReloadMeta() { h.l.RLock() defer h.l.RUnlock() @@ -155,6 +169,7 @@ func (h *MetaHandler) ReloadMeta() { } } +// NewDBMetaHandler returns new database meta refresher instance. func NewDBMetaHandler(dbID string, conn DBHandler) *DBMetaHandler { return &DBMetaHandler{ tableMapping: make(map[string][]string), @@ -163,6 +178,7 @@ func NewDBMetaHandler(dbID string, conn DBHandler) *DBMetaHandler { } } +// ReloadMeta manually reload single database meta (just a cache clean). func (h *DBMetaHandler) ReloadMeta() { h.GetTables() h.l.RLock() @@ -172,6 +188,7 @@ func (h *DBMetaHandler) ReloadMeta() { } } +// CacheGetTables get database tables with cache. func (h *DBMetaHandler) CacheGetTables() (tables []string, err error) { h.l.RLock() tables = append(tables, h.tables...) @@ -183,6 +200,7 @@ func (h *DBMetaHandler) CacheGetTables() (tables []string, err error) { return h.GetTables() } +// GetTables get database tables without cache. func (h *DBMetaHandler) GetTables() (tables []string, err error) { h.l.Lock() defer h.l.Unlock() @@ -213,6 +231,7 @@ func (h *DBMetaHandler) GetTables() (tables []string, err error) { return } +// CacheGetTable get database table columns with cache. func (h *DBMetaHandler) CacheGetTable(tableName string) (columns []string, err error) { h.l.RLock() tableName = strings.ToLower(tableName) @@ -228,6 +247,7 @@ func (h *DBMetaHandler) CacheGetTable(tableName string) (columns []string, err e return h.GetTable(tableName) } +// GetTable get database table columns without cache. func (h *DBMetaHandler) GetTable(tableName string) (columns []string, err error) { h.l.Lock() defer h.l.Unlock() diff --git a/cmd/cql-mysql-adapter/resolver/query.go b/cmd/cql-mysql-adapter/resolver/query.go index 1e040619f..04d41e85a 100644 --- a/cmd/cql-mysql-adapter/resolver/query.go +++ b/cmd/cql-mysql-adapter/resolver/query.go @@ -18,6 +18,7 @@ package resolver import "github.com/CovenantSQL/sqlparser" +// Query defines a resolver result query. type Query struct { Stmt sqlparser.Statement Query string @@ -25,6 +26,7 @@ type Query struct { ResultColumns []string } +// IsDDL returns whether a resolved query is DDL or not. func (q *Query) IsDDL() bool { if q.Stmt != nil { _, ok := q.Stmt.(*sqlparser.DDL) @@ -34,12 +36,11 @@ func (q *Query) IsDDL() bool { return false } +// IsRead returns whether a resolved query is READ query. func (q *Query) IsRead() bool { if q.Stmt != nil { switch q.Stmt.(type) { - case *sqlparser.Show: - return true - case sqlparser.SelectStatement: + case *sqlparser.Show, *sqlparser.Explain, sqlparser.SelectStatement: return true } } diff --git a/cmd/cql-mysql-adapter/resolver/resolver.go b/cmd/cql-mysql-adapter/resolver/resolver.go index 5619456f4..96165c9b7 100644 --- a/cmd/cql-mysql-adapter/resolver/resolver.go +++ b/cmd/cql-mysql-adapter/resolver/resolver.go @@ -26,8 +26,10 @@ import ( ) var ( + // ErrQueryLogicError defines query logic error. ErrQueryLogicError = errors.New("contains logic error in query") - ErrTooMuchQueries = errors.New("too much queries") + // ErrTooMuchQueries defines two much queries in a single request. + ErrTooMuchQueries = errors.New("too much queries") columnsShowCreateTable = []string{"sql"} columnsShowTableInfo = []string{"cid", "name", "type", "notnull", "dflt_value", "pk"} @@ -36,22 +38,26 @@ var ( columnsExplain = []string{"addr", "opcode", "p1", "p2", "p3", "p4", "p5", "comment"} ) +// TableColumnMapping defines a table column mapping object. type TableColumnMapping struct { TableDict map[string]int TableColumns []*TableColumns } +// TableColumns defines a table columns mapping object. type TableColumns struct { Table string Columns []string } +// NewTableColumnMapping returns new table column mapping object. func NewTableColumnMapping() *TableColumnMapping { return &TableColumnMapping{ TableDict: make(map[string]int), } } +// Add table and columns to column mapping object. func (m *TableColumnMapping) Add(table string, columns []string) { if o, ok := m.TableDict[table]; ok { if len(m.TableColumns) > o && m.TableColumns[o].Table == table { @@ -66,6 +72,7 @@ func (m *TableColumnMapping) Add(table string, columns []string) { } } +// AppendColumn appends new records table to column mapping. func (m *TableColumnMapping) AppendColumn(table string, columns []string) { if o, ok := m.TableDict[table]; ok { if len(m.TableColumns) > o && m.TableColumns[o].Table == table { @@ -80,6 +87,7 @@ func (m *TableColumnMapping) AppendColumn(table string, columns []string) { } } +// UnionColumn union and distinct records in table to column mapping. func (m *TableColumnMapping) UnionColumn(table string, columns []string) { if o, ok := m.TableDict[table]; ok { if len(m.TableColumns) > o && m.TableColumns[o].Table == table { @@ -102,11 +110,13 @@ func (m *TableColumnMapping) UnionColumn(table string, columns []string) { } } +// Clear clears the table column mapping object. func (m *TableColumnMapping) Clear() { m.TableDict = make(map[string]int) m.TableColumns = m.TableColumns[:0] } +// Union union distinct two table column mapping object. func (m *TableColumnMapping) Union(o *TableColumnMapping) { if o == nil { return @@ -120,6 +130,7 @@ func (m *TableColumnMapping) Union(o *TableColumnMapping) { } } +// Update update current column mapping object using new records. func (m *TableColumnMapping) Update(o *TableColumnMapping) { if o == nil { return @@ -130,6 +141,7 @@ func (m *TableColumnMapping) Update(o *TableColumnMapping) { } } +// Merge append new records to current column mapping object. func (m *TableColumnMapping) Merge(o *TableColumnMapping) { if o == nil { return @@ -140,6 +152,7 @@ func (m *TableColumnMapping) Merge(o *TableColumnMapping) { } } +// MergeDistinct union distinct two table column mapping object records. func (m *TableColumnMapping) MergeDistinct(o *TableColumnMapping) { if o == nil { return @@ -150,6 +163,7 @@ func (m *TableColumnMapping) MergeDistinct(o *TableColumnMapping) { } } +// Get get table columns from the mapping object. func (m *TableColumnMapping) Get(table string) (columns []string, exists bool) { var o int if o, exists = m.TableDict[table]; exists { @@ -162,32 +176,39 @@ func (m *TableColumnMapping) Get(table string) (columns []string, exists bool) { return } +// Resolver defines the query resolver processor object. type Resolver struct { Meta *MetaHandler } +// NewResolver returns a new query resolver processor object. func NewResolver() *Resolver { return &Resolver{ Meta: NewMetaHandler(), } } +// ReloadMeta reloads underlying meta handler. func (r *Resolver) ReloadMeta() { r.Meta.ReloadMeta() } +// Close close the resolver especially the underlying meta handler. func (r *Resolver) Close() { r.Meta.Stop() } +// RegisterDB register database connection to resolver. func (r *Resolver) RegisterDB(dbID string, conn DBHandler) { r.Meta.AddConn(dbID, conn) } +// ResolveQuery parses string query and returns multiple query resolver result. func (r *Resolver) ResolveQuery(dbID string, query string) (queries []*Query, err error) { return r.resolveQuery(dbID, query, -1) } +// ResolveSingleQuery parse string query and make sure there is only one query in the query string. func (r *Resolver) ResolveSingleQuery(dbID string, query string) (q *Query, err error) { var queries []*Query if queries, err = r.resolveQuery(dbID, query, 1); err == nil && len(queries) > 0 { @@ -196,6 +217,7 @@ func (r *Resolver) ResolveSingleQuery(dbID string, query string) (q *Query, err return } +// Resolve process sqlparser statement and returns resolved result. func (r *Resolver) Resolve(dbID string, stmt sqlparser.Statement) (q *Query, err error) { q = &Query{ Stmt: stmt, @@ -257,6 +279,7 @@ func (r *Resolver) resolveQuery(dbID string, query string, maxQueries int) (quer return } +// BuildParamCount process sqlparser statement and returns the required parameter count in query. func (r *Resolver) BuildParamCount(stmt sqlparser.Statement) (params int, err error) { params = 0 argDedup := make(map[string]bool) @@ -340,6 +363,7 @@ func (r *Resolver) buildResultColumnsWithTableName(tableSeq *int32, dbID string, return } +// BuildResultColumns returns the result columns (detected name and count) of sqlparser statement. func (r *Resolver) BuildResultColumns(tableSeq *int32, dbID string, stmt sqlparser.Statement) (columns []string, err error) { switch s := stmt.(type) { case *sqlparser.Show: diff --git a/cmd/cql-secure-gateway/cursor.go b/cmd/cql-secure-gateway/cursor.go index 5d98cd957..2e3c6f616 100644 --- a/cmd/cql-secure-gateway/cursor.go +++ b/cmd/cql-secure-gateway/cursor.go @@ -18,10 +18,12 @@ package main import "github.com/CovenantSQL/CovenantSQL/cmd/cql-mysql-adapter/cursor" +// Cursor defines wrapper for mysql adapter cursor object. type Cursor struct { *cursor.Cursor } +// NewCursor returns new cursor object. func NewCursor() *Cursor { return &Cursor{ Cursor: cursor.NewCursor(), diff --git a/cmd/cql-secure-gateway/resolver.go b/cmd/cql-secure-gateway/resolver.go index c53b33c59..d21315641 100644 --- a/cmd/cql-secure-gateway/resolver.go +++ b/cmd/cql-secure-gateway/resolver.go @@ -25,27 +25,86 @@ import ( ) var ( - ErrInvalidStatement = errors.New("invalid statement") - ErrInvalidColumn = errors.New("invalid column") - ErrInvalidTable = errors.New("invalid table") - ErrAmbiguousColumnName = errors.New("ambiguous column name") + // ErrQueryLogicError redefines mysql adapter query logic error for convenience. + ErrQueryLogicError = resolver.ErrQueryLogicError ) +const ( + // FilterOp defines the select filter operator. + FilterOp Op = "FILTER" + // DistinctOp the distinct operation in select/aggr function. + DistinctOp Op = "DISTINCT" + // AliasOp defines the table alias/column alias operation. + AliasOp Op = "ALIAS" + // AndOp defines the logical and operation. + AndOp Op = "AND" + // OrOp defines the logical or operation. + OrOp Op = "OR" + // NotOp defines the logical not operation. + NotOp Op = "NOT" + // LiteralOp defines the literal value. + LiteralOp Op = "LITERAL" + // ParameterOp defines the parameter placeholder. + ParameterOp Op = "PARAMETER" + // BinaryOp defines the binary operator such as + / -. + BinaryOp Op = "BINARY" + // UnaryOp defines the unary operator such as ~. + UnaryOp Op = "UNARY" + // CaseConditionOp defines the condition branch of the case expression. + CaseConditionOp Op = "CASE_CONDITION" + // CaseOp defines the expression branch of the case expression. + CaseOp Op = "CASE" + // ComparisonOp defines the comparison expression such as > >= <= =. + ComparisonOp Op = "COMPARISON" + // ConvertOp defines the convert expression. + ConvertOp Op = "CONVERT" + // ExistsOp defines the exists expression. + ExistsOp Op = "EXISTS" + // FunctionOp defines the function call expression such as max/min/count. + FunctionOp Op = "FUNCTION" + // GroupConcatOp defines the group concat function expression. + GroupConcatOp Op = "GROUP_CONCAT" + // IsOp defines the is expression such as is not null/is null/is true/is false. + IsOp Op = "IS" + // RangeOp defines the range compare expression such as between and operator. + RangeOp Op = "RANGE" + // SubQueryOp defines the sub-query expression. + SubQueryOp Op = "SUB_QUERY" + // OrderByOp defines the order by expression. + OrderByOp Op = "ORDER_BY" + // GroupByOp defines the group by expression. + GroupByOp Op = "GROUP_BY" + // JoinUsingOp defines the using operator part of the join expression. + JoinUsingOp Op = "JOIN_USING" + // AssignmentOp defines the assignment operation of insert/update column/values. + AssignmentOp Op = "ASSIGNMENT" +) + +// Op defines the operation type. +type Op string + +// Resolver defines the query resolver process object for secure gateway. type Resolver struct { *resolver.Resolver } +// Query defines the resolved query of the secure gateway. type Query struct { *resolver.Query + PhysicalColumnRely []*ColumnResult } +// Columns defines the column array for resolving. type Columns []*Column +// Computation defines a single column operation. type Computation struct { - Op string + Op Op + Data interface{} Operands Columns } +// ResolveColName resolves column name with qualifier in current columns symbol table. func (c Columns) ResolveColName(col *sqlparser.ColName) (column *Column, err error) { if col == nil { return @@ -65,10 +124,11 @@ func (c Columns) ResolveColName(col *sqlparser.ColName) (column *Column, err err // not found tb := sqlparser.NewTrackedBuffer(nil) - err = errors.Wrapf(ErrInvalidColumn, "column %s not found", tb.WriteNode(col).String()) + err = errors.Wrapf(ErrQueryLogicError, "column %s not found", tb.WriteNode(col).String()) return } +// ResolveColIdent resolves column name in current columns symbol table. func (c Columns) ResolveColIdent(col sqlparser.ColIdent) (column *Column, err error) { found := false for _, item := range c { @@ -78,7 +138,7 @@ func (c Columns) ResolveColIdent(col sqlparser.ColIdent) (column *Column, err er // ambiguous tb1 := sqlparser.NewTrackedBuffer(nil) tb2 := sqlparser.NewTrackedBuffer(nil) - err = errors.Wrapf(ErrAmbiguousColumnName, + err = errors.Wrapf(ErrQueryLogicError, "ambiguous column %s, candidates: %s, %s", col.String(), tb1.WriteNode(&column.ColName).String(), tb2.WriteNode(&item.ColName).String()) @@ -91,27 +151,30 @@ func (c Columns) ResolveColIdent(col sqlparser.ColIdent) (column *Column, err er } if !found { - err = errors.Wrapf(ErrInvalidColumn, "column %s not found", col.String()) + err = errors.Wrapf(ErrQueryLogicError, "column %s not found", col.String()) return } return } +// NewColumns returns new columns symbol table. func NewColumns(cols ...*Column) Columns { return Columns(cols) } +// Column defines the resolved column. type Column struct { ColName sqlparser.ColName IsPhysical bool Computation *Computation } +// ColumnResult defines the resolved column result. type ColumnResult struct { TableName string ColName string - Ops [][]string + Ops [][]Op } func (r *Resolver) buildExpression(dbID string, expr sqlparser.Expr, originColumns Columns) (cols Columns, err error) { @@ -143,7 +206,7 @@ func (r *Resolver) buildExpression(dbID string, expr sqlparser.Expr, originColum cols = NewColumns(&Column{ IsPhysical: false, Computation: &Computation{ - Op: "and", + Op: AndOp, Operands: cols, }, }) @@ -159,7 +222,7 @@ func (r *Resolver) buildExpression(dbID string, expr sqlparser.Expr, originColum cols = NewColumns(&Column{ IsPhysical: false, Computation: &Computation{ - Op: "or", + Op: OrOp, Operands: cols, }, }) @@ -170,34 +233,18 @@ func (r *Resolver) buildExpression(dbID string, expr sqlparser.Expr, originColum cols = NewColumns(&Column{ IsPhysical: false, Computation: &Computation{ - Op: "not", + Op: NotOp, Operands: tempColumns, }, }) case *sqlparser.ParenExpr: return r.buildExpression(dbID, e.Expr, originColumns) - - /* literal constant */ - case sqlparser.BoolVal: - cols = NewColumns(&Column{ - IsPhysical: false, - Computation: &Computation{ - Op: "bool_literal", - }, - }) - case *sqlparser.NullVal: + case sqlparser.BoolVal, *sqlparser.NullVal, *sqlparser.TimeExpr: cols = NewColumns(&Column{ IsPhysical: false, Computation: &Computation{ - Op: "null_literal", - }, - }) - case *sqlparser.TimeExpr: - // treat time expression as literal - cols = NewColumns(&Column{ - IsPhysical: false, - Computation: &Computation{ - Op: "time_literal", + Op: LiteralOp, + Data: e, }, }) case sqlparser.ValTuple: @@ -209,25 +256,20 @@ func (r *Resolver) buildExpression(dbID string, expr sqlparser.Expr, originColum } case *sqlparser.SQLVal: switch e.Type { - case sqlparser.StrVal, sqlparser.HexVal: + case sqlparser.StrVal, sqlparser.HexVal, sqlparser.IntVal, sqlparser.FloatVal, sqlparser.HexNum, sqlparser.BitVal: cols = NewColumns(&Column{ IsPhysical: false, Computation: &Computation{ - Op: "string_literal", - }, - }) - case sqlparser.IntVal, sqlparser.FloatVal, sqlparser.HexNum, sqlparser.BitVal: - cols = NewColumns(&Column{ - IsPhysical: false, - Computation: &Computation{ - Op: "numeric_literal", + Op: LiteralOp, + Data: e, }, }) case sqlparser.ValArg: cols = NewColumns(&Column{ IsPhysical: false, Computation: &Computation{ - Op: "parameter", + Op: ParameterOp, + Data: e, }, }) default: @@ -248,7 +290,7 @@ func (r *Resolver) buildExpression(dbID string, expr sqlparser.Expr, originColum cols = NewColumns(&Column{ IsPhysical: false, Computation: &Computation{ - Op: "binary_op", + Op: BinaryOp, Operands: cols, }, }) @@ -268,7 +310,7 @@ func (r *Resolver) buildExpression(dbID string, expr sqlparser.Expr, originColum cols = append(cols, &Column{ IsPhysical: false, Computation: &Computation{ - Op: "case_condition", + Op: CaseConditionOp, Operands: tempColumns, }, }) @@ -283,7 +325,7 @@ func (r *Resolver) buildExpression(dbID string, expr sqlparser.Expr, originColum cols = NewColumns(&Column{ IsPhysical: false, Computation: &Computation{ - Op: "case", + Op: CaseOp, Operands: cols, }, }) @@ -298,7 +340,7 @@ func (r *Resolver) buildExpression(dbID string, expr sqlparser.Expr, originColum cols = NewColumns(&Column{ IsPhysical: false, Computation: &Computation{ - Op: "compare", + Op: ComparisonOp, Operands: cols, }, }) @@ -309,7 +351,7 @@ func (r *Resolver) buildExpression(dbID string, expr sqlparser.Expr, originColum cols = NewColumns(&Column{ IsPhysical: false, Computation: &Computation{ - Op: "convert", + Op: ConvertOp, Operands: tempColumns, }, }) @@ -322,19 +364,34 @@ func (r *Resolver) buildExpression(dbID string, expr sqlparser.Expr, originColum cols = NewColumns(&Column{ IsPhysical: false, Computation: &Computation{ - Op: "exists", + Op: ExistsOp, Operands: tempColumns, }, }) } case *sqlparser.FuncExpr: + // if the expression is count(*), we resolve to simple count(1) expression + if !e.Distinct && e.Name.EqualString("count") && len(e.Exprs) == 1 { + if v, ok := e.Exprs[0].(*sqlparser.StarExpr); ok { + if v.TableName.IsEmpty() { + cols = NewColumns(&Column{ + IsPhysical: false, + Computation: &Computation{ + Op: FunctionOp, + }, + }) + return + } + } + } + if tempColumns, err = r.buildSelectExprs(dbID, e.Exprs, e.Distinct, originColumns); err != nil { return } cols = NewColumns(&Column{ IsPhysical: false, Computation: &Computation{ - Op: "function", + Op: FunctionOp, Operands: tempColumns, }, }) @@ -343,28 +400,13 @@ func (r *Resolver) buildExpression(dbID string, expr sqlparser.Expr, originColum return } cols = append(cols, tempColumns...) - if tempColumns, err = r.buildOrderBy(dbID, e.OrderBy, cols); err != nil { - return - } - cols = append(cols, tempColumns...) cols = NewColumns(&Column{ IsPhysical: false, Computation: &Computation{ - Op: "group_concat", + Op: GroupConcatOp, Operands: cols, }, }) - case *sqlparser.IntervalExpr: - if tempColumns, err = r.buildExpression(dbID, e.Expr, originColumns); err != nil { - return - } - cols = NewColumns(&Column{ - IsPhysical: false, - Computation: &Computation{ - Op: "interval", - Operands: tempColumns, - }, - }) case *sqlparser.IsExpr: if tempColumns, err = r.buildExpression(dbID, e.Expr, originColumns); err != nil { return @@ -372,30 +414,10 @@ func (r *Resolver) buildExpression(dbID string, expr sqlparser.Expr, originColum cols = NewColumns(&Column{ IsPhysical: false, Computation: &Computation{ - Op: "is", + Op: IsOp, Operands: tempColumns, }, }) - case *sqlparser.SubstrExpr: - if tempColumns, err = r.buildExpression(dbID, e.From, originColumns); err != nil { - return - } - cols = append(cols, tempColumns...) - if tempColumns, err = r.buildExpression(dbID, e.To, originColumns); err != nil { - return - } - cols = append(cols, tempColumns...) - if tempColumns, err = r.buildExpression(dbID, e.Name, originColumns); err != nil { - return - } - cols = append(cols, tempColumns...) - cols = NewColumns(&Column{ - IsPhysical: false, - Computation: &Computation{ - Op: "substr", - Operands: cols, - }, - }) case *sqlparser.RangeCond: for _, expr := range []sqlparser.Expr{e.Left, e.From, e.To} { if tempColumns, err = r.buildExpression(dbID, expr, originColumns); err != nil { @@ -406,7 +428,7 @@ func (r *Resolver) buildExpression(dbID string, expr sqlparser.Expr, originColum cols = NewColumns(&Column{ IsPhysical: false, Computation: &Computation{ - Op: "range", + Op: RangeOp, Operands: cols, }, }) @@ -417,8 +439,8 @@ func (r *Resolver) buildExpression(dbID string, expr sqlparser.Expr, originColum cols = NewColumns(&Column{ IsPhysical: false, Computation: &Computation{ - Op: "unary_op", - Operands: cols, + Op: UnaryOp, + Operands: tempColumns, }, }) case *sqlparser.Subquery: @@ -428,14 +450,14 @@ func (r *Resolver) buildExpression(dbID string, expr sqlparser.Expr, originColum } if len(tempColumns) != 1 { - err = errors.Wrapf(ErrInvalidStatement, "sub query returns %d columns", len(tempColumns)) + err = errors.Wrapf(ErrQueryLogicError, "sub query returns %d columns", len(tempColumns)) return } cols = NewColumns(&Column{ IsPhysical: false, Computation: &Computation{ - Op: "sub_query", + Op: SubQueryOp, Operands: tempColumns, }, }) @@ -458,7 +480,7 @@ func (r *Resolver) buildOrder(dbID string, order *sqlparser.Order, originColumns } if len(cols) != 1 { - err = errors.Wrapf(ErrInvalidStatement, "sub query returns %d columns", len(cols)) + err = errors.Wrapf(ErrQueryLogicError, "sub query returns %d columns", len(cols)) return } @@ -466,7 +488,7 @@ func (r *Resolver) buildOrder(dbID string, order *sqlparser.Order, originColumns cols = NewColumns(&Column{ IsPhysical: false, Computation: &Computation{ - Op: "order_by", + Op: OrderByOp, Operands: cols, }, }) @@ -485,7 +507,7 @@ func (r *Resolver) buildGroupBy(dbID string, groupBy sqlparser.GroupBy, originCo cols = append(cols, &Column{ IsPhysical: false, Computation: &Computation{ - Op: "group_by", + Op: GroupByOp, Operands: tempColumns, }, }) @@ -550,7 +572,7 @@ func (r *Resolver) buildAllColumnInTableExpr(dbID string, tableExpr sqlparser.Ta } default: tb := sqlparser.NewTrackedBuffer(nil) - err = errors.Wrapf(ErrInvalidStatement, "invalid table expression %s", tb.WriteNode(te.Expr).String()) + err = errors.Wrapf(ErrQueryLogicError, "invalid table expression %s", tb.WriteNode(te.Expr).String()) return } @@ -566,7 +588,7 @@ func (r *Resolver) buildAllColumnInTableExpr(dbID string, tableExpr sqlparser.Ta }, IsPhysical: false, Computation: &Computation{ - Op: "alias", + Op: AliasOp, Operands: Columns([]*Column{c}), }, }) @@ -607,7 +629,7 @@ func (r *Resolver) buildAllColumnInTableExpr(dbID string, tableExpr sqlparser.Ta } if !found { - err = errors.Wrapf(ErrInvalidColumn, "column not found %s", uc.String()) + err = errors.Wrapf(ErrQueryLogicError, "column not found %s", uc.String()) return } } @@ -615,7 +637,7 @@ func (r *Resolver) buildAllColumnInTableExpr(dbID string, tableExpr sqlparser.Ta cols = append(cols, &Column{ IsPhysical: false, Computation: &Computation{ - Op: "join_using", + Op: JoinUsingOp, Operands: args, }, }) @@ -638,7 +660,7 @@ func (r *Resolver) buildAllColumnInTableExpr(dbID string, tableExpr sqlparser.Ta return default: tb := sqlparser.NewTrackedBuffer(nil) - err = errors.Wrapf(ErrInvalidStatement, "invalid table expression %s", tb.WriteNode(tableExpr).String()) + err = errors.Wrapf(ErrQueryLogicError, "invalid table expression %s", tb.WriteNode(tableExpr).String()) return } } @@ -672,28 +694,32 @@ func (r *Resolver) buildLimit(dbID string, limitCond *sqlparser.Limit) (cols Col } func (r *Resolver) buildUpdateExprs(dbID string, updateExprs sqlparser.UpdateExprs, originColumns Columns) (cols Columns, err error) { + var ( + tempCol *Column + tempCols Columns + ) for _, ue := range updateExprs { - colTableName := ue.Name.Qualifier.Name.String() - found := false - - for _, oc := range originColumns { - if colTableName != "" && !strings.EqualFold(oc.ColName.Qualifier.Name.String(), colTableName) { - // table name not matched - continue - } + if tempCol, err = originColumns.ResolveColName(ue.Name); err != nil { + return + } - if ue.Name.Name.Equal(oc.ColName.Name) { - // matched - found = true - cols = append(cols, oc) - } + // resolve expression + if tempCols, err = r.buildExpression(dbID, ue.Expr, originColumns); err != nil { + return } - if !found { - tb := sqlparser.NewTrackedBuffer(nil) - err = errors.Wrapf(ErrInvalidColumn, "no such column %s", tb.WriteNode(ue).String()) + if len(tempCols) != 1 { + err = errors.Wrapf(ErrQueryLogicError, "invalid expression column count %d", len(tempCols)) return } + + cols = append(cols, &Column{ + IsPhysical: false, + Computation: &Computation{ + Op: AssignmentOp, + Operands: NewColumns(tempCol, tempCols[0]), + }, + }) } return @@ -713,7 +739,7 @@ func (r *Resolver) buildInsertColumns(dbID string, insertCols sqlparser.Columns, } if !found { - err = errors.Wrapf(ErrInvalidColumn, "no such column %s", ic.String()) + err = errors.Wrapf(ErrQueryLogicError, "no such column %s", ic.String()) return } } @@ -721,25 +747,54 @@ func (r *Resolver) buildInsertColumns(dbID string, insertCols sqlparser.Columns, return } -func (r *Resolver) buildInsertRows(dbID string, insertRows sqlparser.InsertRows) (cols Columns, err error) { +func (r *Resolver) buildInsertRows(dbID string, insertRows sqlparser.InsertRows, insertCols Columns) (cols Columns, err error) { + var tempCols Columns + switch n := insertRows.(type) { case sqlparser.SelectStatement: - return r.buildSelectStatement(dbID, n, nil) + if tempCols, err = r.buildSelectStatement(dbID, n, nil); err != nil { + return + } + if len(tempCols) != len(insertCols) { + err = errors.Wrap(ErrQueryLogicError, "insert column count mismatched") + return + } + // build assignment operations + for i, c := range insertCols { + cols = append(cols, &Column{ + IsPhysical: false, + Computation: &Computation{ + Op: AssignmentOp, + Operands: NewColumns(c, tempCols[i]), + }, + }) + } case sqlparser.Values: // parse values - var tempCols Columns for _, v := range n { if tempCols, err = r.buildExpression(dbID, sqlparser.Expr(v), nil); err != nil { return } - cols = append(cols, tempCols...) + if len(tempCols) != len(insertCols) { + err = errors.Wrap(ErrQueryLogicError, "insert column count mismatched") + return + } + for i, c := range insertCols { + cols = append(cols, &Column{ + IsPhysical: false, + Computation: &Computation{ + Op: AssignmentOp, + Operands: NewColumns(c, tempCols[i]), + }, + }) + } } - return default: tb := sqlparser.NewTrackedBuffer(nil) - err = errors.Wrapf(ErrInvalidStatement, "invalid insert values type %s", tb.WriteNode(insertRows).String()) - return + err = errors.Wrapf(ErrQueryLogicError, "invalid insert values type %s", tb.WriteNode(insertRows).String()) } + + return } func (r *Resolver) buildSelectExpr(dbID string, selectExpr sqlparser.SelectExpr, hasDistinct bool, originColumns Columns) (cols Columns, err error) { @@ -752,7 +807,7 @@ func (r *Resolver) buildSelectExpr(dbID string, selectExpr sqlparser.SelectExpr, // must be one column result if len(tempCols) != 1 { - err = errors.Wrapf(ErrInvalidStatement, "sub query returns %d columns", len(tempCols)) + err = errors.Wrapf(ErrQueryLogicError, "sub query returns %d columns", len(tempCols)) return } @@ -769,7 +824,7 @@ func (r *Resolver) buildSelectExpr(dbID string, selectExpr sqlparser.SelectExpr, }, IsPhysical: false, Computation: &Computation{ - Op: "alias", + Op: AliasOp, Operands: Columns{tempCols[0]}, }, }) @@ -788,7 +843,7 @@ func (r *Resolver) buildSelectExpr(dbID string, selectExpr sqlparser.SelectExpr, } if len(starColumns) == 0 { - err = errors.Wrapf(ErrInvalidTable, "no such table %s", e.TableName.Name.String()) + err = errors.Wrapf(ErrQueryLogicError, "no such table %s", e.TableName.Name.String()) return } @@ -796,20 +851,22 @@ func (r *Resolver) buildSelectExpr(dbID string, selectExpr sqlparser.SelectExpr, } } - cols = func() Columns { - newCols := NewColumns() - for _, c := range cols { - newCols = append(newCols, &Column{ - ColName: c.ColName, - IsPhysical: false, - Computation: &Computation{ - Op: "distinct", - Operands: cols, - }, - }) - } - return newCols - }() + if hasDistinct { + cols = func() Columns { + newCols := NewColumns() + for _, c := range cols { + newCols = append(newCols, &Column{ + ColName: c.ColName, + IsPhysical: false, + Computation: &Computation{ + Op: DistinctOp, + Operands: cols, + }, + }) + } + return newCols + }() + } return } @@ -832,7 +889,7 @@ func (r *Resolver) filterDependentCols(originColumns Columns) (cols Columns) { for _, c := range originColumns { if !c.IsPhysical { // do not add dependency that only contains aliases - if c.Computation != nil && c.Computation.Op == "alias" { + if c.Computation != nil && c.Computation.Op == AliasOp { tempCols := r.filterDependentCols(c.Computation.Operands) if len(tempCols) == 0 { continue @@ -912,7 +969,7 @@ func (r *Resolver) buildSelectStatement(dbID string, stmt sqlparser.SelectStatem ColName: c.ColName, IsPhysical: false, Computation: &Computation{ - Op: "filter", + Op: FilterOp, Operands: cols, }, }) @@ -950,28 +1007,28 @@ func (r *Resolver) buildSelectStatement(dbID string, stmt sqlparser.SelectStatem } return newCols }() - case *sqlparser.ParenSelect: - // not supported in sqlite - err = errors.Wrap(ErrInvalidStatement, "invalid paren select statement") + default: + err = errors.Wrap(ErrQueryLogicError, "invalid select statement") } return } +// GetDependentPhysicalColumns returns the physical table dependency column result. func (r *Resolver) GetDependentPhysicalColumns(dbID string, stmt sqlparser.Statement) (columns []*ColumnResult, err error) { var ( cols Columns columnMap = make(map[string]map[string]int) - visit func(opStack []string, cols Columns) + visit func(opStack []Op, cols Columns) ) columns = make([]*ColumnResult, 0) if cols, err = r.BuildPhysicalColumnsTransformations(dbID, stmt); err != nil { return } - visit = func(opStack []string, cols Columns) { + visit = func(opStack []Op, cols Columns) { for _, c := range cols { if !c.IsPhysical { if c.Computation != nil { - subStack := make([]string, len(opStack)+1) + subStack := make([]Op, len(opStack)+1) subStack[0] = c.Computation.Op copy(subStack[1:], opStack) visit(subStack, c.Computation.Operands) @@ -989,7 +1046,7 @@ func (r *Resolver) GetDependentPhysicalColumns(dbID string, stmt sqlparser.State columns = append(columns, &ColumnResult{ TableName: tableName, ColName: colName, - Ops: [][]string{opStack}, + Ops: [][]Op{opStack}, }) } else { // exists @@ -1002,6 +1059,7 @@ func (r *Resolver) GetDependentPhysicalColumns(dbID string, stmt sqlparser.State return } +// BuildPhysicalColumnsTransformations returns the physical column dependency tree. func (r *Resolver) BuildPhysicalColumnsTransformations(dbID string, stmt sqlparser.Statement) (cols Columns, err error) { var ( tempCols Columns @@ -1016,21 +1074,32 @@ func (r *Resolver) BuildPhysicalColumnsTransformations(dbID string, stmt sqlpars if originCols, err = r.buildAllColumnInTable(dbID, n.Table); err != nil { return } - // insert columns can be projected - if tempCols, err = r.buildInsertColumns(dbID, n.Columns, originCols); err != nil { - return + // no columns specified + if len(n.Columns) == 0 { + // use all columns + cols = append(cols, originCols...) + } else { + // insert columns can be projected + if tempCols, err = r.buildInsertColumns(dbID, n.Columns, originCols); err != nil { + return + } + if len(tempCols) > len(originCols) { + err = errors.Wrapf(ErrQueryLogicError, "invalid column count for table insert") + return + } + cols = append(cols, tempCols...) } - cols = append(cols, tempCols...) // insert values has no relation to the table - if tempCols, err = r.buildInsertRows(dbID, n.Rows); err != nil { + if tempCols, err = r.buildInsertRows(dbID, n.Rows, cols); err != nil { return } cols = append(cols, tempCols...) - // update expression can be projected, on duplication expression is the same as update expression - if tempCols, err = r.buildUpdateExprs(dbID, sqlparser.UpdateExprs(n.OnDup), originCols); err != nil { + + if len(n.OnDup) != 0 { + // not supported + err = errors.Wrapf(ErrQueryLogicError, "on duplicate/conflict statement is not supported") return } - cols = append(cols, tempCols...) case *sqlparser.Update: // in update, origin cols is always physical cols if originCols, err = r.buildAllColumnInTableExprs(dbID, n.TableExprs, nil); err != nil { @@ -1078,8 +1147,89 @@ func (r *Resolver) BuildPhysicalColumnsTransformations(dbID string, stmt sqlpars // no physical column rely, but requires extra privilege default: // invalid statement - err = errors.Wrap(ErrInvalidStatement, "unknown statement") + err = errors.Wrap(ErrQueryLogicError, "unknown statement") } return } + +// ResolveQuery parses string query and returns multiple query resolver result. +func (r *Resolver) ResolveQuery(dbID string, query string) (q []*Query, err error) { + var ( + queries []*resolver.Query + colResult []*ColumnResult + ) + + if queries, err = r.Resolver.ResolveQuery(dbID, query); err != nil { + return + } + + for _, query := range queries { + if colResult, err = r.GetDependentPhysicalColumns(dbID, query.Stmt); err != nil { + return + } + + q = append(q, &Query{ + Query: query, + PhysicalColumnRely: colResult, + }) + } + + return +} + +// ResolveSingleQuery parse string query and make sure there is only one query in the query string. +func (r *Resolver) ResolveSingleQuery(dbID string, queryStr string) (q *Query, err error) { + var ( + query *resolver.Query + colResult []*ColumnResult + ) + + if query, err = r.Resolver.ResolveSingleQuery(dbID, queryStr); err != nil { + return + } + + if colResult, err = r.GetDependentPhysicalColumns(dbID, query.Stmt); err != nil { + return + } + + q = &Query{ + Query: query, + PhysicalColumnRely: colResult, + } + return +} + +// Resolve process sqlparser statement and returns resolved result. +func (r *Resolver) Resolve(dbID string, stmt sqlparser.Statement) (q *Query, err error) { + var ( + query *resolver.Query + colResult []*ColumnResult + ) + + if query, err = r.Resolver.Resolve(dbID, stmt); err != nil { + return + } + + if colResult, err = r.GetDependentPhysicalColumns(dbID, stmt); err != nil { + return + } + + q = &Query{ + Query: query, + PhysicalColumnRely: colResult, + } + return +} + +// IsShowOrExplain returns if the query is show or explain statement. +func (q *Query) IsShowOrExplain() bool { + if q.Stmt != nil { + switch q.Stmt.(type) { + case *sqlparser.Show, *sqlparser.Explain: + return true + } + } + + return false +} diff --git a/cmd/cql-secure-gateway/resolver_test.go b/cmd/cql-secure-gateway/resolver_test.go index 544fbde40..1d0502a78 100644 --- a/cmd/cql-secure-gateway/resolver_test.go +++ b/cmd/cql-secure-gateway/resolver_test.go @@ -17,10 +17,11 @@ package main import ( + "testing" + "github.com/CovenantSQL/CovenantSQL/cmd/cql-mysql-adapter/resolver" "github.com/CovenantSQL/sqlparser" . "github.com/smartystreets/goconvey/convey" - "testing" ) func newColName(table string, col string) sqlparser.ColName { @@ -103,7 +104,7 @@ func TestColumnResolver(t *testing.T) { "CREATE TABLE t1(a INTEGER, b INTEGER, c INTEGER, d INTEGER, e INTEGER)", "CREATE TABLE tab0(col0 INTEGER, col1 INTEGER, col2 INTEGER)", "CREATE TABLE tab1(col0 INTEGER, col1 INTEGER, col2 INTEGER)", - "CREATE TABLE tab2(col0 INTEGER, col1 INTEGER, col2 INTEGER)", + "CREATE TABLE tab2(col0 INTEGER, col1 INTEGER, col2 INTEGER, col3 INTEGER)", "CREATE TABLE tab3(pk INTEGER PRIMARY KEY, col0 INTEGER, col1 FLOAT, col2 TEXT, col3 INTEGER, col4 FLOAT, col5 TEXT)", }) So(err, ShouldBeNil) @@ -183,35 +184,36 @@ func TestColumnResolver(t *testing.T) { So(err, ShouldBeNil) So(cols, ShouldHaveLength, 2) + // READ STATEMENTS colResults, err = r.GetDependentPhysicalColumns(dbID, mustParseStatement(c, ` SELECT c-d, - abs(b-c) - FROM t1 - WHERE (c<=d-2 OR c>=d+2) - AND e+d BETWEEN a+b-10 AND c+130 - AND b IS NOT NULL`)) + abs(b-c) + FROM t1 +WHERE (c<=d-2 OR c>=d+2) + AND e+d BETWEEN a+b-10 AND c+130 + AND b IS NOT NULL`)) So(err, ShouldBeNil) So(colResults, ShouldHaveLength, 5) colResults, err = r.GetDependentPhysicalColumns(dbID, mustParseStatement(c, ` SELECT a-b, - (SELECT count(1) FROM t1 AS x WHERE x.ba AND ea AND e= ( NULL )`)) + So(err, ShouldBeNil) + So(colResults, ShouldHaveLength, 2) + colResults, err = r.GetDependentPhysicalColumns(dbID, mustParseStatement(c, ` SELECT pk FROM tab3 WHERE ((col4 > 901.6) AND col0 >= 17 AND (col0 IS NULL) AND col0 < 649) AND (col3 > 960) OR (col3 > 58) ORDER BY 1 DESC`)) So(err, ShouldBeNil) So(colResults, ShouldHaveLength, 4) colResults, err = r.GetDependentPhysicalColumns(dbID, mustParseStatement(c, ` -SELECT substr(pk, 1, 1), group_concat(pk), cast(pk as string) FROM tab3 +SELECT pk FROM tab3 WHERE (col0 >= 93) AND (((col1 >= 55.99 OR (col3 <= 1) OR (((col3 >= 24))) AND +col3 IN (84,26,92,96) OR ((col3 >= 53)) AND (col4 >= 6.22) AND (col0 >= 33 OR (((((col0 >= 6)) AND +((col3 < 27 AND (((col0 > 78))))) OR col1 > 90.47 OR col3 IN (47,79) AND col1 > 57.55 OR ((col1 < 77.85)) OR (col3 = 36 OR col0 < 90) AND col3 <= 44))) +OR col1 IN (30.98,34.53,44.58,62.81,85.85) OR col3 IS NULL) AND (col3 IN (37,6,58,52,90,51) OR (col3 >= 55)))) AND ((col1 = 46.0 OR col3 <= 24)) +AND ((((col0 < 75)) AND col4 >= 77.95)) OR (col0 < 40) OR ((((((col3 IS NULL))) OR (col3 > 61) AND col3 > 30)) OR col1 < 11.20) AND col1 >= 17.42 +AND col0 >= 96 OR col3 > 47 OR (((col3 BETWEEN 4 AND 79) AND col3 >= 51)))`)) + So(err, ShouldBeNil) + So(colResults, ShouldHaveLength, 5) + + // join statement + colResults, err = r.GetDependentPhysicalColumns(dbID, mustParseStatement(c, ` +SELECT COUNT( * ) + 30 AS col2 FROM tab0 AS cor0 CROSS JOIN tab0`)) + So(err, ShouldBeNil) + So(colResults, ShouldHaveLength, 0) + + // join with using + colResults, err = r.GetDependentPhysicalColumns(dbID, mustParseStatement(c, ` +SELECT COUNT(1) FROM tab0 JOIN tab1 USING(col0)`)) + So(err, ShouldBeNil) + So(colResults, ShouldHaveLength, 2) + + // join with on condition + colResults, err = r.GetDependentPhysicalColumns(dbID, mustParseStatement(c, ` +SELECT + MIN ( cor0.col2 + 36 ) FROM tab2 AS cor0 JOIN tab0 cor1 ON NOT + - 81 >= + CAST ( NULL AS INT ) + - 6`)) + So(err, ShouldBeNil) + So(colResults, ShouldHaveLength, 1) + + // paren from table statement + colResults, err = r.GetDependentPhysicalColumns(dbID, mustParseStatement(c, ` +SELECT * FROM (tab0, tab2)`)) + So(err, ShouldBeNil) + So(colResults, ShouldHaveLength, 7) + + // group_concat and cast statement + colResults, err = r.GetDependentPhysicalColumns(dbID, mustParseStatement(c, ` +SELECT substr(1, 2, 3), group_concat(pk), cast(pk as string) FROM tab3 `)) So(err, ShouldBeNil) So(colResults, ShouldHaveLength, 1) + // literals colResults, err = r.GetDependentPhysicalColumns(dbID, mustParseStatement(c, ` -SELECT true, null, current_timestamp, not false +SELECT true, null, current_timestamp, not false, "1" `)) So(err, ShouldBeNil) So(colResults, ShouldHaveLength, 0) - colResults, err = r.GetDependentPhysicalColumns(dbID, mustParseStatement(c, ` + // ambiguous column from two tables + _, err = r.GetDependentPhysicalColumns(dbID, mustParseStatement(c, ` SELECT col0 FROM tab0, tab1 `)) So(err, ShouldNotBeNil) - colResults, err = r.GetDependentPhysicalColumns(dbID, mustParseStatement(c, ` + // invalid column without table reference + _, err = r.GetDependentPhysicalColumns(dbID, mustParseStatement(c, ` SELECT col1024 FROM tab0 `)) So(err, ShouldNotBeNil) + + // invalid column with table reference + _, err = r.GetDependentPhysicalColumns(dbID, mustParseStatement(c, ` +SELECT tab0.col1024 FROM tab0`)) + So(err, ShouldNotBeNil) + + // and left expression failure + _, err = r.GetDependentPhysicalColumns(dbID, mustParseStatement(c, ` +SELECT (tab0.col1024) AND (tab0.col0) FROM tab0`)) + So(err, ShouldNotBeNil) + + // and right expression failure + _, err = r.GetDependentPhysicalColumns(dbID, mustParseStatement(c, ` +SELECT (tab0.col0) AND (tab0.col1024) FROM tab0`)) + So(err, ShouldNotBeNil) + + // or left expression failure + _, err = r.GetDependentPhysicalColumns(dbID, mustParseStatement(c, ` +SELECT (tab0.col1024) OR (tab0.col0) FROM tab0`)) + So(err, ShouldNotBeNil) + + // or right expression failure + _, err = r.GetDependentPhysicalColumns(dbID, mustParseStatement(c, ` +SELECT (tab0.col0) OR (tab0.col1024) FROM tab0`)) + So(err, ShouldNotBeNil) + + // not expression failure + _, err = r.GetDependentPhysicalColumns(dbID, mustParseStatement(c, ` +SELECT NOT tab0.col1024 FROM tab0`)) + So(err, ShouldNotBeNil) + + // value tuple failure + _, err = r.GetDependentPhysicalColumns(dbID, mustParseStatement(c, ` +SELECT tab0.col0 IN (tab0.col1024) FROM tab0`)) + So(err, ShouldNotBeNil) + + // invalid binary left expression + _, err = r.GetDependentPhysicalColumns(dbID, mustParseStatement(c, ` +SELECT tab0.col1024 + tab0.col0 FROM tab0`)) + So(err, ShouldNotBeNil) + + // invalid binary right expression + _, err = r.GetDependentPhysicalColumns(dbID, mustParseStatement(c, ` +SELECT tab0.col0 + tab0.col1024 FROM tab0`)) + So(err, ShouldNotBeNil) + + // invalid case condition expression + _, err = r.GetDependentPhysicalColumns(dbID, mustParseStatement(c, ` +SELECT CASE tab0.col1024 WHEN 1 THEN 1 ELSE 0 END FROM tab0`)) + So(err, ShouldNotBeNil) + + // invalid case result expression + _, err = r.GetDependentPhysicalColumns(dbID, mustParseStatement(c, ` +SELECT CASE tab0.col0 WHEN 1 THEN tab0.col1024 ELSE 2 END FROM tab0`)) + So(err, ShouldNotBeNil) + + // invalid expression to cast + _, err = r.GetDependentPhysicalColumns(dbID, mustParseStatement(c, ` +SELECT CAST(tab0.col1024 AS string) FROM tab0`)) + So(err, ShouldNotBeNil) + + // invalid sub query of exists + _, err = r.GetDependentPhysicalColumns(dbID, mustParseStatement(c, ` +SELECT COUNT(1) FROM tab0 WHERE EXISTS (SELECT x.col1024 FROM tab0 AS x WHERE tab0.col0 = x.col1024)`)) + So(err, ShouldNotBeNil) + + // invalid function argument + _, err = r.GetDependentPhysicalColumns(dbID, mustParseStatement(c, ` +SELECT MAX(tab0.col1024) FROM tab0`)) + So(err, ShouldNotBeNil) + + // invalid group concat argument + _, err = r.GetDependentPhysicalColumns(dbID, mustParseStatement(c, ` +SELECT GROUP_CONCAT(tab0.col1024) FROM tab0`)) + So(err, ShouldNotBeNil) + + // interval expression is not supported + _, err = r.GetDependentPhysicalColumns(dbID, mustParseStatement(c, ` +SELECT INTERVAL 3 MONTH`)) + So(err, ShouldNotBeNil) + + // invalid is expression argument + _, err = r.GetDependentPhysicalColumns(dbID, mustParseStatement(c, ` +SELECT tab0.col1024 IS NULL FROM tab0 +`)) + So(err, ShouldNotBeNil) + + // invalid expression in between + _, err = r.GetDependentPhysicalColumns(dbID, mustParseStatement(c, ` +SELECT tab0.col1024 BETWEEN 1 AND 2 FROM tab0`)) + So(err, ShouldNotBeNil) + + // invalid unary expression + _, err = r.GetDependentPhysicalColumns(dbID, mustParseStatement(c, ` +SELECT ~tab0.col1024 FROM tab0`)) + So(err, ShouldNotBeNil) + + // invalid sub query expression + _, err = r.GetDependentPhysicalColumns(dbID, mustParseStatement(c, ` +SELECT (SELECT tab0.col1024 FROM tab0)`)) + So(err, ShouldNotBeNil) + + // invalid sub query column count + _, err = r.GetDependentPhysicalColumns(dbID, mustParseStatement(c, ` +SELECT (SELECT tab0.col0, tab0.col1 FROM tab0)`)) + So(err, ShouldNotBeNil) + + // invalid order by statement + _, err = r.GetDependentPhysicalColumns(dbID, mustParseStatement(c, ` +SELECT * FROM tab0 ORDER BY tab0.col1024 ASC`)) + So(err, ShouldNotBeNil) + + // invalid order by using two column expression + _, err = r.GetDependentPhysicalColumns(dbID, mustParseStatement(c, ` +SELECT * FROM tab0 ORDER BY (1, 2)`)) + So(err, ShouldNotBeNil) + + // invalid group by column expression + _, err = r.GetDependentPhysicalColumns(dbID, mustParseStatement(c, ` +SELECT COUNT(1) FROM tab0 GROUP BY tab0.col1024`)) + So(err, ShouldNotBeNil) + + // unknown table + _, err = r.GetDependentPhysicalColumns(dbID, mustParseStatement(c, ` +SELECT * FROM tab1000`)) + So(err, ShouldNotBeNil) + + // invalid sub query in from statement + _, err = r.GetDependentPhysicalColumns(dbID, mustParseStatement(c, ` +SELECT * FROM (SELECT tab0.col1024 FROM tab0)`)) + So(err, ShouldNotBeNil) + + // invalid join left table statement + _, err = r.GetDependentPhysicalColumns(dbID, mustParseStatement(c, ` +SELECT * FROM (SELECT tab0.col1024 FROM tab0) JOIN tab1 ON col1024 = tab1.col1`)) + So(err, ShouldNotBeNil) + + // invalid join right table statement + _, err = r.GetDependentPhysicalColumns(dbID, mustParseStatement(c, ` +SELECT * FROM tab1 JOIN (SELECT tab0.col1024 FROM tab0) ON col1024 = tab1.col1`)) + So(err, ShouldNotBeNil) + + // invalid join using statement + _, err = r.GetDependentPhysicalColumns(dbID, mustParseStatement(c, ` +SELECT * FROM tab0 JOIN tab2 USING (col3)`)) + So(err, ShouldNotBeNil) + + // invalid join on statement + _, err = r.GetDependentPhysicalColumns(dbID, mustParseStatement(c, ` +SELECT * FROM tab0 JOIN tab2 ON tab0.col3 = tab2.col3`)) + So(err, ShouldNotBeNil) + + // invalid paren table statement + _, err = r.GetDependentPhysicalColumns(dbID, mustParseStatement(c, ` +SELECT * FROM (tab0, tab1024)`)) + So(err, ShouldNotBeNil) + + // invalid limit statement + _, err = r.GetDependentPhysicalColumns(dbID, mustParseStatement(c, ` +SELECT * FROM tab0 LIMIT (SELECT COUNT(1) FROM tab1024)`)) + So(err, ShouldNotBeNil) + + _, err = r.GetDependentPhysicalColumns(dbID, mustParseStatement(c, ` +SELECT * FROM tab0 LIMIT (SELECT COUNT(1) FROM tab1024), 1`)) + So(err, ShouldNotBeNil) + + // invalid select column alias + _, err = r.GetDependentPhysicalColumns(dbID, mustParseStatement(c, ` +SELECT (1, 2) as A`)) + So(err, ShouldNotBeNil) + + // invalid star select + _, err = r.GetDependentPhysicalColumns(dbID, mustParseStatement(c, ` +SELECT t.* FROM tab0`)) + So(err, ShouldNotBeNil) + + // invalid having statement + _, err = r.GetDependentPhysicalColumns(dbID, mustParseStatement(c, ` +SELECT * FROM tab0 GROUP BY col0 HAVING count(col1024) > 0`)) + So(err, ShouldNotBeNil) + + // invalid union left statement + _, err = r.GetDependentPhysicalColumns(dbID, mustParseStatement(c, ` +SELECT * FROM tab1024 UNION SELECT * FROM tab0`)) + So(err, ShouldNotBeNil) + + // invalid union right statement + _, err = r.GetDependentPhysicalColumns(dbID, mustParseStatement(c, ` +SELECT * FROM tab0 UNION SELECT * FROM tab1024`)) + So(err, ShouldNotBeNil) + + // invalid union order by statement + _, err = r.GetDependentPhysicalColumns(dbID, mustParseStatement(c, ` +SELECT * FROM tab0 UNION SELECT * FROM tab1 ORDER BY col1024`)) + So(err, ShouldNotBeNil) + + // invalid union limit statement + _, err = r.GetDependentPhysicalColumns(dbID, mustParseStatement(c, ` +SELECT * FROM tab0 UNION SELECT * FROM tab1 LIMIT 1, (SELECT * FROM tab1024)`)) + So(err, ShouldNotBeNil) + + // WRITE STATEMENTS + colResults, err = r.GetDependentPhysicalColumns(dbID, mustParseStatement(c, ` +INSERT INTO tab0 VALUES(1, 2, 3)`)) + So(err, ShouldBeNil) + So(colResults, ShouldHaveLength, 3) + + colResults, err = r.GetDependentPhysicalColumns(dbID, mustParseStatement(c, ` +INSERT INTO tab0 (col0) VALUES(1)`)) + So(err, ShouldBeNil) + So(colResults, ShouldHaveLength, 1) + + colResults, err = r.GetDependentPhysicalColumns(dbID, mustParseStatement(c, ` +INSERT INTO tab1 (col0) VALUES((SELECT COUNT(col1) FROM tab2))`)) + So(err, ShouldBeNil) + So(colResults, ShouldHaveLength, 2) + + colResults, err = r.GetDependentPhysicalColumns(dbID, mustParseStatement(c, ` +INSERT INTO tab1 (col0) SELECT col1 FROM tab2`)) + So(err, ShouldBeNil) + So(colResults, ShouldHaveLength, 2) + + // invalid insert table + _, err = r.GetDependentPhysicalColumns(dbID, mustParseStatement(c, ` +INSERT INTO tab1024 VALUES(1, 1, 1, 1, 1)`)) + So(err, ShouldNotBeNil) + + // column mismatch in columns + _, err = r.GetDependentPhysicalColumns(dbID, mustParseStatement(c, ` +INSERT INTO tab0 (col0, col1, col2, col0) VALUES(1, 1, 1, 1)`)) + So(err, ShouldNotBeNil) + + // non-existent column + _, err = r.GetDependentPhysicalColumns(dbID, mustParseStatement(c, ` +INSERT INTO tab0 (col1024) VALUES(1)`)) + So(err, ShouldNotBeNil) + + // column count not matched in rows + _, err = r.GetDependentPhysicalColumns(dbID, mustParseStatement(c, ` +INSERT INTO tab0 (col0) VALUES(1, 1)`)) + So(err, ShouldNotBeNil) + + // column count not matched in sub query + _, err = r.GetDependentPhysicalColumns(dbID, mustParseStatement(c, ` +INSERT INTO tab0 (col0) SELECT * FROM tab1`)) + So(err, ShouldNotBeNil) + + // invalid insert rows expression + _, err = r.GetDependentPhysicalColumns(dbID, mustParseStatement(c, ` +INSERT INTO tab0 (col0) VALUES(col1)`)) + So(err, ShouldNotBeNil) + + // invalid insert sub query expression + _, err = r.GetDependentPhysicalColumns(dbID, mustParseStatement(c, ` +INSERT INTO tab0 (col0) SELECT * FROM tab1024`)) + So(err, ShouldNotBeNil) + + // update expression + colResults, err = r.GetDependentPhysicalColumns(dbID, mustParseStatement(c, ` +UPDATE tab0 SET col0 = 1 WHERE col1 = 2 ORDER BY col2 DESC LIMIT 10`)) + So(err, ShouldBeNil) + So(colResults, ShouldHaveLength, 3) + + colResults, err = r.GetDependentPhysicalColumns(dbID, mustParseStatement(c, ` +UPDATE tab0 SET col0 = col0 + 1`)) + So(err, ShouldBeNil) + So(colResults, ShouldHaveLength, 1) + + colResults, err = r.GetDependentPhysicalColumns(dbID, mustParseStatement(c, ` +UPDATE tab0 SET col0 = col0 + col1`)) + So(err, ShouldBeNil) + So(colResults, ShouldHaveLength, 2) + + // invalid table expression + _, err = r.GetDependentPhysicalColumns(dbID, mustParseStatement(c, ` +UPDATE tab1024 SET col0 = 1`)) + So(err, ShouldNotBeNil) + + _, err = r.GetDependentPhysicalColumns(dbID, mustParseStatement(c, ` +UPDATE tab0 SET col1024 = 1`)) + So(err, ShouldNotBeNil) + + _, err = r.GetDependentPhysicalColumns(dbID, mustParseStatement(c, ` +UPDATE tab0 SET col0 = (SELECT col1024 FROM tab0)`)) + So(err, ShouldNotBeNil) + + _, err = r.GetDependentPhysicalColumns(dbID, mustParseStatement(c, ` +UPDATE tab0 SET col1 = 1 WHERE col1024 = 1`)) + So(err, ShouldNotBeNil) + + _, err = r.GetDependentPhysicalColumns(dbID, mustParseStatement(c, ` +UPDATE tab0 SET col1 = 1 ORDER BY col1024`)) + So(err, ShouldNotBeNil) + + _, err = r.GetDependentPhysicalColumns(dbID, mustParseStatement(c, ` +UPDATE tab0 SET col1 = 1 LIMIT (SELECT col1024 FROM tab0)`)) + So(err, ShouldNotBeNil) + + _, err = r.GetDependentPhysicalColumns(dbID, mustParseStatement(c, ` +UPDATE tab0 SET col1 = (1, 2)`)) + So(err, ShouldNotBeNil) + + // delete expression + colResults, err = r.GetDependentPhysicalColumns(dbID, mustParseStatement(c, ` +DELETE FROM tab0 WHERE col0 = 1 ORDER BY col0 LIMIT 10`)) + So(err, ShouldBeNil) + So(colResults, ShouldHaveLength, 3) + + colResults, err = r.GetDependentPhysicalColumns(dbID, mustParseStatement(c, ` +DELETE FROM tab0 WHERE EXISTS (SELECT col1 FROM tab1)`)) + So(err, ShouldBeNil) + So(colResults, ShouldHaveLength, 4) + + // invalid table to delete + _, err = r.GetDependentPhysicalColumns(dbID, mustParseStatement(c, ` +DELETE FROM tab1024`)) + So(err, ShouldNotBeNil) + + _, err = r.GetDependentPhysicalColumns(dbID, mustParseStatement(c, ` +DELETE FROM tab0 WHERE EXISTS (SELECT col0 FROM tab1024)`)) + So(err, ShouldNotBeNil) + + _, err = r.GetDependentPhysicalColumns(dbID, mustParseStatement(c, ` +DELETE FROM tab0 ORDER BY col1024 LIMIT 100`)) + So(err, ShouldNotBeNil) + + _, err = r.GetDependentPhysicalColumns(dbID, mustParseStatement(c, ` +DELETE FROM tab0 LIMIT (SELECT col1024 FROM tab0)`)) + So(err, ShouldNotBeNil) + + cols, err = r.BuildPhysicalColumnsTransformations(dbID, mustParseStatement(c, `INSERT INTO tab0 (col0) VALUES(?)`)) + So(err, ShouldBeNil) }) } diff --git a/cmd/cql-secure-gateway/xxx_test.go b/cmd/cql-secure-gateway/xxx_test.go index 6f0c93df4..fbf089e57 100644 --- a/cmd/cql-secure-gateway/xxx_test.go +++ b/cmd/cql-secure-gateway/xxx_test.go @@ -19,6 +19,7 @@ package main import ( "crypto/rand" "database/sql" + "github.com/CovenantSQL/CovenantSQL/crypto/hash" _ "github.com/CovenantSQL/go-sqlite3-encrypt" "github.com/CovenantSQL/sqlparser" From 0dd169f8bfb8045bb9b11285a318a2d311644036 Mon Sep 17 00:00:00 2001 From: Qi Xiao Date: Thu, 29 Nov 2018 13:45:23 +0800 Subject: [PATCH 16/24] Refactor mysql-adapter module dependency --- cmd/cql-mysql-adapter/cursor/cursor.go | 110 ++++--------------- cmd/cql-mysql-adapter/cursor/handler.go | 46 ++++++++ cmd/cql-mysql-adapter/handler.go | 119 +++++++++++++++++++++ cmd/cql-mysql-adapter/resolver/meta.go | 30 +++++- cmd/cql-mysql-adapter/resolver/query.go | 46 ++++++++ cmd/cql-mysql-adapter/resolver/resolver.go | 29 ++--- cmd/cql-mysql-adapter/server.go | 4 +- 7 files changed, 276 insertions(+), 108 deletions(-) create mode 100644 cmd/cql-mysql-adapter/cursor/handler.go create mode 100644 cmd/cql-mysql-adapter/handler.go diff --git a/cmd/cql-mysql-adapter/cursor/cursor.go b/cmd/cql-mysql-adapter/cursor/cursor.go index 618ee2e0e..66deef8e4 100644 --- a/cmd/cql-mysql-adapter/cursor/cursor.go +++ b/cmd/cql-mysql-adapter/cursor/cursor.go @@ -24,8 +24,6 @@ import ( "strings" "sync" - "github.com/CovenantSQL/CovenantSQL/client" - "github.com/CovenantSQL/CovenantSQL/cmd/cql-mysql-adapter/resolver" "github.com/CovenantSQL/CovenantSQL/utils/log" my "github.com/siddontang/go-mysql/mysql" ) @@ -53,16 +51,15 @@ var ( // Cursor is a mysql connection handler, like a cursor of normal database. type Cursor struct { - curUser string - curDBLock sync.RWMutex - curDB string - curDBInstance *sql.DB - curResolver *resolver.Resolver + curUser string + curDBLock sync.RWMutex + curDB string + h Handler } // NewCursor returns a new cursor. -func NewCursor() (c *Cursor) { - return &Cursor{} +func NewCursor(h Handler) (c *Cursor) { + return &Cursor{h: h} } func (c *Cursor) buildResultSet(rows *sql.Rows) (r *my.Result, err error) { @@ -101,21 +98,6 @@ func (c *Cursor) getCurDB() string { return c.curDB } -func (c *Cursor) ensureDatabase() (conn *sql.DB, rsv *resolver.Resolver, err error) { - c.curDBLock.Lock() - defer c.curDBLock.Unlock() - - if c.curDB == "" { - err = my.NewError(my.ER_NO_DB_ERROR, "select database before any query") - return - } - - conn = c.curDBInstance - rsv = c.curResolver - - return -} - func (c *Cursor) detectColumnType(typeStr string) (typeByte uint8) { typeStr = strings.ToUpper(typeStr) @@ -283,28 +265,7 @@ func (c *Cursor) UseDB(dbName string) (err error) { return } - // close previous database and resolver - if c.curResolver != nil { - c.curResolver.Close() - } - if c.curDBInstance != nil { - c.curDBInstance.Close() - } - - // connect database - cfg := client.NewConfig() - cfg.DatabaseID = dbName - - var db *sql.DB - - if db, err = sql.Open("covenantsql", cfg.FormatDSN()); err != nil { - return - } - - c.curDB = dbName - c.curDBInstance = db - c.curResolver = resolver.NewResolver() - c.curResolver.RegisterDB(dbName, c.curDBInstance) + err = c.h.EnsureDatabase(dbName) return } @@ -320,17 +281,9 @@ func (c *Cursor) HandleQuery(query string) (r *my.Result, err error) { return } - var ( - conn *sql.DB - rsv *resolver.Resolver - q *resolver.Query - ) + var q Query - if conn, rsv, err = c.ensureDatabase(); err != nil { - return - } - - if q, err = rsv.ResolveSingleQuery(c.getCurDB(), query); err != nil { + if q, err = c.h.Resolve(c.getCurDB(), query); err != nil { err = my.NewError(my.ER_SYNTAX_ERROR, err.Error()) return } @@ -338,7 +291,7 @@ func (c *Cursor) HandleQuery(query string) (r *my.Result, err error) { // normal query if q.IsRead() { var rows *sql.Rows - if rows, err = conn.Query(q.Query); err != nil { + if rows, err = c.h.Query(q); err != nil { err = my.NewError(my.ER_UNKNOWN_ERROR, err.Error()) return } @@ -348,7 +301,7 @@ func (c *Cursor) HandleQuery(query string) (r *my.Result, err error) { } var result sql.Result - if result, err = conn.Exec(q.Query); err != nil { + if result, err = c.h.Exec(q); err != nil { err = my.NewError(my.ER_UNKNOWN_ERROR, err.Error()) return } @@ -363,24 +316,14 @@ func (c *Cursor) HandleQuery(query string) (r *my.Result, err error) { Resultset: nil, } - if q.IsDDL() { - rsv.ReloadMeta() - } - return } // HandleFieldList handle COM_FILED_LIST command. func (c *Cursor) HandleFieldList(table string, fieldWildcard string) (fields []*my.Field, err error) { - var conn *sql.DB - - if conn, _, err = c.ensureDatabase(); err != nil { - return - } - // send show tables command var columns *sql.Rows - if columns, err = conn.Query(fmt.Sprintf("DESC `%s`", table)); err != nil { + if columns, err = c.h.QueryString(c.getCurDB(), fmt.Sprintf("DESC `%s`", table)); err != nil { // wrap error err = my.NewError(my.ER_UNKNOWN_ERROR, err.Error()) return @@ -455,16 +398,9 @@ func (c *Cursor) HandleStmtPrepare(query string) (params int, columns int, conte return } - var ( - rsv *resolver.Resolver - q *resolver.Query - ) - - if _, rsv, err = c.ensureDatabase(); err != nil { - return - } + var q Query - if q, err = rsv.ResolveSingleQuery(c.getCurDB(), query); err != nil { + if q, err = c.h.Resolve(c.getCurDB(), query); err != nil { err = my.NewError(my.ER_SYNTAX_ERROR, err.Error()) return } @@ -473,8 +409,8 @@ func (c *Cursor) HandleStmtPrepare(query string) (params int, columns int, conte return } - params = q.ParamCount - columns = len(q.ResultColumns) + params = q.GetParamCount() + columns = q.GetResultColumnCount() context = q return @@ -483,30 +419,24 @@ func (c *Cursor) HandleStmtPrepare(query string) (params int, columns int, conte // HandleStmtExecute handle COM_STMT_EXECUTE, context is the previous one set in prepare // query is the statement prepare query, and args is the params for this statement. func (c *Cursor) HandleStmtExecute(context interface{}, query string, args []interface{}) (r *my.Result, err error) { - var q *resolver.Query + var q Query switch v := context.(type) { case *my.Result: // special query r = v return - case *resolver.Query: + case Query: q = v default: err = my.NewError(my.ER_UNKNOWN_ERROR, "invalid prepared statement") return } - var conn *sql.DB - - if conn, _, err = c.ensureDatabase(); err != nil { - return - } - // normal query if q.IsRead() { var rows *sql.Rows - if rows, err = conn.Query(q.Query, args...); err != nil { + if rows, err = c.h.Query(q, args...); err != nil { err = my.NewError(my.ER_UNKNOWN_ERROR, err.Error()) return } @@ -516,7 +446,7 @@ func (c *Cursor) HandleStmtExecute(context interface{}, query string, args []int } var result sql.Result - if result, err = conn.Exec(q.Query, args...); err != nil { + if result, err = c.h.Exec(q, args...); err != nil { err = my.NewError(my.ER_UNKNOWN_ERROR, err.Error()) return } diff --git a/cmd/cql-mysql-adapter/cursor/handler.go b/cmd/cql-mysql-adapter/cursor/handler.go new file mode 100644 index 000000000..025de4da4 --- /dev/null +++ b/cmd/cql-mysql-adapter/cursor/handler.go @@ -0,0 +1,46 @@ +/* + * Copyright 2018 The CovenantSQL Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package cursor + +import ( + "database/sql" + + "github.com/CovenantSQL/sqlparser" +) + +// Query defines resolved query utilized by cursor object. +type Query interface { + IsRead() bool + IsDDL() bool + IsShow() bool + IsExplain() bool + GetDatabase() string + GetQuery() string + GetStmt() sqlparser.Statement + GetParamCount() int + GetResultColumnCount() int +} + +// Handler defines callback function utilized by cursor object. +type Handler interface { + EnsureDatabase(dbID string) error + Resolve(dbID string, query string) (Query, error) + Query(q Query, args ...interface{}) (*sql.Rows, error) + Exec(q Query, args ...interface{}) (sql.Result, error) + QueryString(dbID string, query string, args ...interface{}) (*sql.Rows, error) + ExecString(dbID string, query string, args ...interface{}) (sql.Result, error) +} diff --git a/cmd/cql-mysql-adapter/handler.go b/cmd/cql-mysql-adapter/handler.go new file mode 100644 index 000000000..bb0bf3a58 --- /dev/null +++ b/cmd/cql-mysql-adapter/handler.go @@ -0,0 +1,119 @@ +/* + * Copyright 2018 The CovenantSQL Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package main + +import ( + "database/sql" + "sync" + + "github.com/CovenantSQL/CovenantSQL/client" + "github.com/CovenantSQL/CovenantSQL/cmd/cql-mysql-adapter/cursor" + "github.com/CovenantSQL/CovenantSQL/cmd/cql-mysql-adapter/resolver" + "github.com/pingcap/errors" +) + +// Handler defines the mysql adapter query handler. +type Handler struct { + l sync.Mutex + r *resolver.Resolver +} + +// NewHandler returns the new mysql adapter handler instance. +func NewHandler() *Handler { + return &Handler{ + r: resolver.NewResolver(), + } +} + +// EnsureDatabase returns whether a database is valid or not. +func (h *Handler) EnsureDatabase(dbID string) (err error) { + _, err = h.ensureDatabase(dbID) + return +} + +// Resolve resolves query to ast and various parse results. +func (h *Handler) Resolve(dbID string, query string) (q cursor.Query, err error) { + if _, err = h.ensureDatabase(dbID); err != nil { + return + } + if q, err = h.r.ResolveSingleQuery(dbID, query); err != nil { + return + } + return +} + +// Query executes a resolved read query. +func (h *Handler) Query(q cursor.Query, args ...interface{}) (rows *sql.Rows, err error) { + if !q.IsRead() { + err = errors.Wrapf(resolver.ErrQueryLogicError, "not a read query") + return + } + return h.QueryString(q.GetDatabase(), q.GetQuery(), args...) +} + +// Exec executes a resolved write query. +func (h *Handler) Exec(q cursor.Query, args ...interface{}) (res sql.Result, err error) { + if q.IsRead() { + err = errors.Wrapf(resolver.ErrQueryLogicError, "not a write query") + return + } + if q.IsDDL() { + defer h.r.ReloadMeta() + } + return h.ExecString(q.GetDatabase(), q.GetQuery(), args...) +} + +// QueryString executes a string query without resolving. +func (h *Handler) QueryString(dbID string, query string, args ...interface{}) (rows *sql.Rows, err error) { + var db resolver.DBHandler + if db, err = h.ensureDatabase(dbID); err != nil { + return + } + return db.Query(query, args...) +} + +// ExecString executes a string query without resolving. +func (h *Handler) ExecString(dbID string, query string, args ...interface{}) (result sql.Result, err error) { + var db resolver.DBHandler + if db, err = h.ensureDatabase(dbID); err != nil { + return + } + return db.Exec(query, args...) +} + +func (h *Handler) ensureDatabase(dbID string) (db resolver.DBHandler, err error) { + var exists bool + if db, exists = h.r.GetDB(dbID); !exists { + // new connection + cfg := client.NewConfig() + cfg.DatabaseID = dbID + if db, err = sql.Open("covenantsql", cfg.FormatDSN()); err != nil { + return + } + if !h.r.RegisterDB(dbID, db) { + db.Close() + } + db, _ = h.r.GetDB(dbID) + } + + return +} + +// Close close the resolver. +func (h *Handler) Close() { + h.r.Close() +} diff --git a/cmd/cql-mysql-adapter/resolver/meta.go b/cmd/cql-mysql-adapter/resolver/meta.go index f2760bd1d..d3a0d3ef8 100644 --- a/cmd/cql-mysql-adapter/resolver/meta.go +++ b/cmd/cql-mysql-adapter/resolver/meta.go @@ -42,6 +42,8 @@ var ( // DBHandler defines registered database connection referenced in meta resolver. type DBHandler interface { Query(query string, args ...interface{}) (rows *sql.Rows, err error) + Exec(query string, args ...interface{}) (result sql.Result, err error) + Close() error } // DBMetaHandler defines single database meta resolve handler. @@ -99,14 +101,27 @@ func (h *MetaHandler) Stop() { }) } +// Close close all registered databases. +func (h *MetaHandler) Close() { + h.l.Lock() + defer h.l.Unlock() + + for k := range h.dbMap { + h.dbMap[k].Close() + delete(h.dbMap, k) + } +} + // AddConn add new database connection to meta refresher. -func (h *MetaHandler) AddConn(dbID string, conn DBHandler) { - h.l.RLock() +func (h *MetaHandler) AddConn(dbID string, conn DBHandler) (added bool) { + h.l.Lock() + defer h.l.Unlock() _, exists := h.dbMap[dbID] - h.l.RUnlock() if !exists { - h.SetConn(dbID, conn) + h.dbMap[dbID] = NewDBMetaHandler(dbID, conn) + added = true } + return } // SetConn set new database connection to meta refresher. @@ -282,3 +297,10 @@ func (h *DBMetaHandler) GetTable(tableName string) (columns []string, err error) return } + +// Close close the underlying database connection. +func (h *DBMetaHandler) Close() { + h.l.Lock() + defer h.l.Unlock() + h.conn.Close() +} diff --git a/cmd/cql-mysql-adapter/resolver/query.go b/cmd/cql-mysql-adapter/resolver/query.go index 04d41e85a..bc3f00e02 100644 --- a/cmd/cql-mysql-adapter/resolver/query.go +++ b/cmd/cql-mysql-adapter/resolver/query.go @@ -20,12 +20,38 @@ import "github.com/CovenantSQL/sqlparser" // Query defines a resolver result query. type Query struct { + Database string Stmt sqlparser.Statement Query string ParamCount int ResultColumns []string } +// GetDatabase returns current database id. +func (q *Query) GetDatabase() string { + return q.Database +} + +// GetParamCount returns current query parameter count. +func (q *Query) GetParamCount() int { + return q.ParamCount +} + +// GetResultColumnCount returns current query result column count. +func (q *Query) GetResultColumnCount() int { + return len(q.ResultColumns) +} + +// GetQuery returns parser refined query string. +func (q *Query) GetQuery() string { + return q.Query +} + +// GetStmt returns parser resolved statement ast. +func (q *Query) GetStmt() sqlparser.Statement { + return q.Stmt +} + // IsDDL returns whether a resolved query is DDL or not. func (q *Query) IsDDL() bool { if q.Stmt != nil { @@ -47,3 +73,23 @@ func (q *Query) IsRead() bool { return false } + +// IsShow returns whether a resolved query is a show query. +func (q *Query) IsShow() bool { + if q.Stmt != nil { + _, ok := q.Stmt.(*sqlparser.Show) + return ok + } + + return false +} + +// IsExplain returns whether a resolved query is a explain query. +func (q *Query) IsExplain() bool { + if q.Stmt != nil { + _, ok := q.Stmt.(*sqlparser.Explain) + return ok + } + + return false +} diff --git a/cmd/cql-mysql-adapter/resolver/resolver.go b/cmd/cql-mysql-adapter/resolver/resolver.go index 96165c9b7..4dbc5483e 100644 --- a/cmd/cql-mysql-adapter/resolver/resolver.go +++ b/cmd/cql-mysql-adapter/resolver/resolver.go @@ -195,12 +195,20 @@ func (r *Resolver) ReloadMeta() { // Close close the resolver especially the underlying meta handler. func (r *Resolver) Close() { + // stop meta refresher r.Meta.Stop() + // close all underlying database connection + r.Meta.Close() } // RegisterDB register database connection to resolver. -func (r *Resolver) RegisterDB(dbID string, conn DBHandler) { - r.Meta.AddConn(dbID, conn) +func (r *Resolver) RegisterDB(dbID string, conn DBHandler) (added bool) { + return r.Meta.AddConn(dbID, conn) +} + +// GetDB returns database connection of a database. +func (r *Resolver) GetDB(dbID string) (DBHandler, bool) { + return r.Meta.GetConn(dbID) } // ResolveQuery parses string query and returns multiple query resolver result. @@ -220,7 +228,8 @@ func (r *Resolver) ResolveSingleQuery(dbID string, query string) (q *Query, err // Resolve process sqlparser statement and returns resolved result. func (r *Resolver) Resolve(dbID string, stmt sqlparser.Statement) (q *Query, err error) { q = &Query{ - Stmt: stmt, + Stmt: stmt, + Database: dbID, } if q.ParamCount, err = r.BuildParamCount(stmt); err != nil { return @@ -261,18 +270,12 @@ func (r *Resolver) resolveQuery(dbID string, query string, maxQueries int) (quer return } - q = &Query{ - Stmt: stmt, - Query: query[lastPos : tokenizer.Position-1], - } - lastPos = tokenizer.Position + 1 - if q.ParamCount, err = r.BuildParamCount(stmt); err != nil { - return - } - var tableSeq int32 - if q.ResultColumns, err = r.BuildResultColumns(&tableSeq, dbID, stmt); err != nil { + if q, err = r.Resolve(dbID, stmt); err != nil { return } + + q.Query = query[lastPos : tokenizer.Position-1] + lastPos = tokenizer.Position + 1 queries = append(queries, q) } diff --git a/cmd/cql-mysql-adapter/server.go b/cmd/cql-mysql-adapter/server.go index 63372d0b1..6e3142529 100644 --- a/cmd/cql-mysql-adapter/server.go +++ b/cmd/cql-mysql-adapter/server.go @@ -28,6 +28,7 @@ import ( type Server struct { listenAddr string listener net.Listener + h *Handler mysqlUser string mysqlPassword string } @@ -38,6 +39,7 @@ func NewServer(listenAddr string, user string, password string) (s *Server, err listenAddr: listenAddr, mysqlUser: user, mysqlPassword: password, + h: NewHandler(), } if s.listener, err = net.Listen("tcp", listenAddr); err != nil { @@ -60,7 +62,7 @@ func (s *Server) Serve() { } func (s *Server) handleConn(conn net.Conn) { - cur := cursor.NewCursor() + cur := cursor.NewCursor(s.h) h, err := mys.NewConn(conn, s.mysqlUser, s.mysqlPassword, cur) if err != nil { From ebc25e5743cf60799eef024d339c5663bb77105b Mon Sep 17 00:00:00 2001 From: Qi Xiao Date: Fri, 30 Nov 2018 14:11:07 +0800 Subject: [PATCH 17/24] Update deps to use new sqlparset statr revision --- Gopkg.lock | 43 +- .../{ => handler}/handler.go | 0 cmd/cql-secure-gateway/cursor.go | 31 - cmd/cql-secure-gateway/utils.go | 58 - .../CovenantSQL/sqlparser/analyzer.go | 4 +- .../github.com/CovenantSQL/sqlparser/ast.go | 92 +- .../github.com/CovenantSQL/sqlparser/sql.go | 2154 ++++++++--------- vendor/github.com/CovenantSQL/sqlparser/sql.y | 22 +- .../github.com/CovenantSQL/sqlparser/token.go | 32 +- vendor/github.com/miekg/dns/README.md | 49 +- vendor/github.com/miekg/dns/acceptfunc.go | 54 + .../github.com/miekg/dns/compress_generate.go | 198 -- vendor/github.com/miekg/dns/defaults.go | 2 +- vendor/github.com/miekg/dns/dns.go | 28 +- vendor/github.com/miekg/dns/dnssec.go | 4 +- vendor/github.com/miekg/dns/doc.go | 107 +- vendor/github.com/miekg/dns/edns.go | 33 +- vendor/github.com/miekg/dns/msg.go | 602 ++--- vendor/github.com/miekg/dns/msg_generate.go | 21 +- vendor/github.com/miekg/dns/msg_helpers.go | 30 +- vendor/github.com/miekg/dns/nsecx.go | 47 +- vendor/github.com/miekg/dns/privaterr.go | 20 +- vendor/github.com/miekg/dns/rawmsg.go | 49 - vendor/github.com/miekg/dns/reverse.go | 5 + vendor/github.com/miekg/dns/server.go | 41 +- vendor/github.com/miekg/dns/sig0.go | 4 +- vendor/github.com/miekg/dns/tsig.go | 2 +- vendor/github.com/miekg/dns/types.go | 65 +- vendor/github.com/miekg/dns/types_generate.go | 16 +- vendor/github.com/miekg/dns/version.go | 2 +- vendor/github.com/miekg/dns/zcompress.go | 152 -- vendor/github.com/miekg/dns/zmsg.go | 1156 ++++----- vendor/github.com/miekg/dns/ztypes.go | 306 +-- vendor/github.com/pingcap/errors/LICENSE | 23 + vendor/github.com/pingcap/errors/README.md | 52 + vendor/github.com/pingcap/errors/appveyor.yml | 32 + vendor/github.com/pingcap/errors/errors.go | 324 +++ vendor/github.com/pingcap/errors/group.go | 42 + .../github.com/pingcap/errors/juju_adaptor.go | 100 + vendor/github.com/pingcap/errors/stack.go | 226 ++ vendor/github.com/prometheus/procfs/Makefile | 56 +- .../prometheus/procfs/Makefile.common | 223 ++ vendor/github.com/prometheus/procfs/go.mod | 1 + .../goleveldb/leveldb/comparer/comparer.go | 2 +- .../syndtr/goleveldb/leveldb/db_compaction.go | 24 +- vendor/golang.org/x/sys/unix/mkall.sh | 22 +- vendor/golang.org/x/sys/unix/mksyscall.go | 367 +++ vendor/golang.org/x/sys/unix/mksyscall.pl | 341 --- .../x/sys/unix/zsyscall_darwin_386.go | 2 +- .../x/sys/unix/zsyscall_darwin_amd64.go | 2 +- .../x/sys/unix/zsyscall_darwin_arm.go | 2 +- .../x/sys/unix/zsyscall_darwin_arm64.go | 2 +- .../x/sys/unix/zsyscall_dragonfly_amd64.go | 2 +- .../x/sys/unix/zsyscall_freebsd_386.go | 2 +- .../x/sys/unix/zsyscall_freebsd_amd64.go | 2 +- .../x/sys/unix/zsyscall_freebsd_arm.go | 2 +- .../x/sys/unix/zsyscall_linux_386.go | 2 +- .../x/sys/unix/zsyscall_linux_amd64.go | 2 +- .../x/sys/unix/zsyscall_linux_arm.go | 2 +- .../x/sys/unix/zsyscall_linux_arm64.go | 2 +- .../x/sys/unix/zsyscall_linux_mips.go | 2 +- .../x/sys/unix/zsyscall_linux_mips64.go | 2 +- .../x/sys/unix/zsyscall_linux_mips64le.go | 2 +- .../x/sys/unix/zsyscall_linux_mipsle.go | 2 +- .../x/sys/unix/zsyscall_linux_ppc64.go | 2 +- .../x/sys/unix/zsyscall_linux_ppc64le.go | 2 +- .../x/sys/unix/zsyscall_linux_riscv64.go | 2 +- .../x/sys/unix/zsyscall_linux_s390x.go | 2 +- .../x/sys/unix/zsyscall_linux_sparc64.go | 204 +- .../x/sys/unix/zsyscall_netbsd_386.go | 2 +- .../x/sys/unix/zsyscall_netbsd_amd64.go | 2 +- .../x/sys/unix/zsyscall_netbsd_arm.go | 2 +- .../x/sys/unix/zsyscall_openbsd_386.go | 2 +- .../x/sys/unix/zsyscall_openbsd_amd64.go | 2 +- .../x/sys/unix/zsyscall_openbsd_arm.go | 2 +- vendor/gopkg.in/yaml.v2/encode.go | 28 + 76 files changed, 4145 insertions(+), 3403 deletions(-) rename cmd/cql-mysql-adapter/{ => handler}/handler.go (100%) delete mode 100644 cmd/cql-secure-gateway/cursor.go delete mode 100644 cmd/cql-secure-gateway/utils.go create mode 100644 vendor/github.com/miekg/dns/acceptfunc.go delete mode 100644 vendor/github.com/miekg/dns/compress_generate.go delete mode 100644 vendor/github.com/miekg/dns/rawmsg.go delete mode 100644 vendor/github.com/miekg/dns/zcompress.go create mode 100644 vendor/github.com/pingcap/errors/LICENSE create mode 100644 vendor/github.com/pingcap/errors/README.md create mode 100644 vendor/github.com/pingcap/errors/appveyor.yml create mode 100644 vendor/github.com/pingcap/errors/errors.go create mode 100644 vendor/github.com/pingcap/errors/group.go create mode 100644 vendor/github.com/pingcap/errors/juju_adaptor.go create mode 100644 vendor/github.com/pingcap/errors/stack.go create mode 100644 vendor/github.com/prometheus/procfs/Makefile.common create mode 100644 vendor/github.com/prometheus/procfs/go.mod create mode 100644 vendor/golang.org/x/sys/unix/mksyscall.go delete mode 100755 vendor/golang.org/x/sys/unix/mksyscall.pl diff --git a/Gopkg.lock b/Gopkg.lock index ee0c26d53..383788339 100644 --- a/Gopkg.lock +++ b/Gopkg.lock @@ -32,7 +32,7 @@ [[projects]] branch = "master" - digest = "1:519e30d8e2f43fcde79a23993cd4e8fe609841d8e7666cc26f8dff0d2613f2c4" + digest = "1:72eed3fbb752e4ff388da196ff30a50a0e15524b3a2b12c6ab6430b89eb67f1e" name = "github.com/CovenantSQL/sqlparser" packages = [ ".", @@ -42,7 +42,7 @@ "dependency/sqltypes", ] pruneopts = "UT" - revision = "765b8d4e9426de3b49bfb96e520093504fb0d509" + revision = "78095ffffebc9ad7ece9e07a0acfa5afdd3a0505" [[projects]] branch = "master" @@ -122,7 +122,7 @@ name = "github.com/btcsuite/btcd" packages = ["btcec"] pruneopts = "UT" - revision = "3dcf298fed2d5fd65918dc560b3942b2aa0629e8" + revision = "7d2daa5bfef28c5e282571bc06416516936115ee" [[projects]] branch = "master" @@ -339,12 +339,12 @@ version = "v1.0.1" [[projects]] - digest = "1:b20d8767957e40b9302a8caaee88caa698513fbd972509a54555302c3aebfa04" + digest = "1:702ff5d8a0196ccb0627f35996efd1081be00c8ae1719402adbffc6e0f8f59ac" name = "github.com/miekg/dns" packages = ["."] pruneopts = "UT" - revision = "7064f7248f5fa5fd79382a76328b4e200b79e4ae" - version = "v1.0.15" + revision = "7586a3cbe8ccfc63f82de3ab2ceeb08c9939af72" + version = "v1.1.1" [[projects]] branch = "master" @@ -362,6 +362,14 @@ revision = "bb6d471dc95d4fe11e432687f8b70ff496cf3136" version = "v1.0.0" +[[projects]] + digest = "1:ea261e1479d92802a6e274d33c126d54c12edec06c36bf0999d04aeca936f184" + name = "github.com/pingcap/errors" + packages = ["."] + pruneopts = "UT" + revision = "1176802fff62540cc87d289bd40c52a2d6b2ea16" + version = "v0.11.0" + [[projects]] digest = "1:40e195917a951a8bf867cd05de2a46aaf1806c50cf92eebf4c16f78cd196f747" name = "github.com/pkg/errors" @@ -404,7 +412,7 @@ [[projects]] branch = "master" - digest = "1:ef74914912f99c79434d9c09658274678bc85080ebe3ab32bec3940ebce5e1fc" + digest = "1:9ea97158ea06d60b1f324aa384f6a4cbd596a81b3cae994e17108b195f398d01" name = "github.com/prometheus/procfs" packages = [ ".", @@ -413,7 +421,7 @@ "xfs", ] pruneopts = "UT" - revision = "619930b0b4713cc1280189bf0a4c54b3fb506f60" + revision = "aa55a523dc0a8297edf51bb75e8eec13eb3be45d" [[projects]] branch = "master" @@ -500,7 +508,7 @@ [[projects]] branch = "master" - digest = "1:2c4379920afd63d262751932c18dfaa30d92ee8241403a919cd7a3e4cf9561d6" + digest = "1:685fdfea42d825ebd39ee0994354b46c374cf2c2b2d97a41a8dee1807c6a9b62" name = "github.com/syndtr/goleveldb" packages = [ "leveldb", @@ -517,7 +525,7 @@ "leveldb/util", ] pruneopts = "UT" - revision = "d5f8e13ca70d404acb9f9f8daab06b3818425145" + revision = "b001fa50d6b27f3f0bb175a87d0cb55426d0a0ae" [[projects]] digest = "1:d9eb1d964a449cf51455fdac2ec3451c664af8e8b93c23d9c31d22b84ea82fde" @@ -611,7 +619,7 @@ "ssh/terminal", ] pruneopts = "UT" - revision = "e657309f52e71501f9934566ac06dc5c2f7f11a1" + revision = "eb0de9b17e854e9b1ccd9963efafc79862359959" [[projects]] branch = "master" @@ -628,26 +636,26 @@ "ipv6", ] pruneopts = "UT" - revision = "adae6a3d119ae4890b46832a2e88a95adc62b8e7" + revision = "351d144fa1fc0bd934e2408202be0c29f25e35a0" [[projects]] branch = "master" - digest = "1:f343f077a5b0bc3a3788b3a04e24dd417e3e25b2acb529c413e212d2c42416ef" + digest = "1:0c7cb58af944f9690af91474a75371eecfe54c1ce3771a8dc08924430ee785d3" name = "golang.org/x/sys" packages = [ "unix", "windows", ] pruneopts = "UT" - revision = "62eef0e2fa9b2c385f7b2778e763486da6880d37" + revision = "4ed8d59d0b35e1e29334a206d1b3f38b1e5dfb31" [[projects]] - digest = "1:342378ac4dcb378a5448dd723f0784ae519383532f5e70ade24132c4c8693202" + digest = "1:4d2e5a73dc1500038e504a8d78b986630e3626dc027bc030ba5c75da257cdb96" name = "gopkg.in/yaml.v2" packages = ["."] pruneopts = "UT" - revision = "5420a8b6744d3b0345ab293f6fcba19c978f1183" - version = "v2.2.1" + revision = "51d6538a90f86fe93ac480b35f37b2be17fef232" + version = "v2.2.2" [solve-meta] analyzer-name = "dep" @@ -677,6 +685,7 @@ "github.com/lufia/iostat", "github.com/miekg/dns", "github.com/minio/blake2b-simd", + "github.com/pingcap/errors", "github.com/pkg/errors", "github.com/prometheus/client_golang/prometheus", "github.com/prometheus/client_model/go", diff --git a/cmd/cql-mysql-adapter/handler.go b/cmd/cql-mysql-adapter/handler/handler.go similarity index 100% rename from cmd/cql-mysql-adapter/handler.go rename to cmd/cql-mysql-adapter/handler/handler.go diff --git a/cmd/cql-secure-gateway/cursor.go b/cmd/cql-secure-gateway/cursor.go deleted file mode 100644 index 2e3c6f616..000000000 --- a/cmd/cql-secure-gateway/cursor.go +++ /dev/null @@ -1,31 +0,0 @@ -/* - * Copyright 2018 The CovenantSQL Authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package main - -import "github.com/CovenantSQL/CovenantSQL/cmd/cql-mysql-adapter/cursor" - -// Cursor defines wrapper for mysql adapter cursor object. -type Cursor struct { - *cursor.Cursor -} - -// NewCursor returns new cursor object. -func NewCursor() *Cursor { - return &Cursor{ - Cursor: cursor.NewCursor(), - } -} diff --git a/cmd/cql-secure-gateway/utils.go b/cmd/cql-secure-gateway/utils.go deleted file mode 100644 index e64d05980..000000000 --- a/cmd/cql-secure-gateway/utils.go +++ /dev/null @@ -1,58 +0,0 @@ -/* - * Copyright 2018 The CovenantSQL Authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package main - -import ( - "path/filepath" - "strings" - - "github.com/pkg/errors" -) - -func fieldMatches(f1 string, f2 string) (matched bool, err error) { - // split field to parts - parts1 := strings.Split(f1, ".") - parts2 := strings.Split(f2, ".") - - if len(parts1) != 3 { - err = errors.Wrapf(ErrInvalidField, "field %s is invalid", f1) - return - } - if len(parts2) != 3 { - err = errors.Wrapf(ErrInvalidField, "field %s is invalid", f2) - return - } - - for i := 0; i != 3; i++ { - var m1, m2 bool - if m1, err = filepath.Match(parts1[i], parts2[i]); err != nil { - err = errors.Wrapf(err, "match field part %s and %s failed", parts1[i], parts2[i]) - return - } - if m2, err = filepath.Match(parts2[i], parts1[i]); err != nil { - err = errors.Wrapf(err, "match field part %s and %s failed", parts1[i], parts2[i]) - return - } - if !m1 && !m2 { - // not matched - return - } - } - - matched = true - return -} diff --git a/vendor/github.com/CovenantSQL/sqlparser/analyzer.go b/vendor/github.com/CovenantSQL/sqlparser/analyzer.go index eff1c8f89..3a8471556 100644 --- a/vendor/github.com/CovenantSQL/sqlparser/analyzer.go +++ b/vendor/github.com/CovenantSQL/sqlparser/analyzer.go @@ -173,7 +173,7 @@ func IsValue(node Expr) bool { switch v := node.(type) { case *SQLVal: switch v.Type { - case StrVal, HexVal, IntVal, ValArg: + case StrVal, HexVal, IntVal, ValArg, PosArg: return true } } @@ -212,7 +212,7 @@ func NewPlanValue(node Expr) (sqltypes.PlanValue, error) { switch node := node.(type) { case *SQLVal: switch node.Type { - case ValArg: + case ValArg, PosArg: return sqltypes.PlanValue{Key: string(node.Val[1:])}, nil case IntVal: n, err := sqltypes.NewIntegral(string(node.Val)) diff --git a/vendor/github.com/CovenantSQL/sqlparser/ast.go b/vendor/github.com/CovenantSQL/sqlparser/ast.go index 7ccdc87ba..014d914af 100644 --- a/vendor/github.com/CovenantSQL/sqlparser/ast.go +++ b/vendor/github.com/CovenantSQL/sqlparser/ast.go @@ -94,6 +94,36 @@ func ParseNext(tokenizer *Tokenizer) (Statement, error) { return tokenizer.ParseTree, nil } +// ParseMultiple returns multiple parsed original query and parsed statement. +func ParseMultiple(tokenizer *Tokenizer) (queries []string, statements []Statement, err error) { + var lastPos int + + for { + if tokenizer.lastChar == ';' { + tokenizer.next() + tokenizer.skipBlank() + lastPos = tokenizer.Position - 1 + } + if tokenizer.lastChar == eofChar { + break + } + + tokenizer.reset() + tokenizer.multi = true + if yyParse(tokenizer) != 0 && tokenizer.partialDDL == nil { + err = tokenizer.LastError + return + } + if tokenizer.partialDDL != nil { + statements = append(statements, tokenizer.partialDDL) + } else { + statements = append(statements, tokenizer.ParseTree) + } + queries = append(queries, string(tokenizer.buf[lastPos:tokenizer.Position-1])) + } + return +} + // SplitStatement returns the first sql statement up to either a ; or EOF // and the remainder from the given buffer func SplitStatement(blob string) (string, string, error) { @@ -1767,33 +1797,33 @@ type Expr interface { SQLNode } -func (*AndExpr) iExpr() {} -func (*OrExpr) iExpr() {} -func (*NotExpr) iExpr() {} -func (*ParenExpr) iExpr() {} -func (*ComparisonExpr) iExpr() {} -func (*RangeCond) iExpr() {} -func (*IsExpr) iExpr() {} -func (*ExistsExpr) iExpr() {} -func (*SQLVal) iExpr() {} -func (*NullVal) iExpr() {} -func (BoolVal) iExpr() {} -func (*ColName) iExpr() {} -func (ValTuple) iExpr() {} -func (*Subquery) iExpr() {} -func (ListArg) iExpr() {} -func (*BinaryExpr) iExpr() {} -func (*UnaryExpr) iExpr() {} -func (*IntervalExpr) iExpr() {} -func (*CollateExpr) iExpr() {} -func (*FuncExpr) iExpr() {} -func (*CaseExpr) iExpr() {} -func (*ValuesFuncExpr) iExpr() {} -func (*ConvertExpr) iExpr() {} -func (*MatchExpr) iExpr() {} -func (*GroupConcatExpr) iExpr() {} -func (*Default) iExpr() {} -func (*TimeExpr) iExpr() {} +func (*AndExpr) iExpr() {} +func (*OrExpr) iExpr() {} +func (*NotExpr) iExpr() {} +func (*ParenExpr) iExpr() {} +func (*ComparisonExpr) iExpr() {} +func (*RangeCond) iExpr() {} +func (*IsExpr) iExpr() {} +func (*ExistsExpr) iExpr() {} +func (*SQLVal) iExpr() {} +func (*NullVal) iExpr() {} +func (BoolVal) iExpr() {} +func (*ColName) iExpr() {} +func (ValTuple) iExpr() {} +func (*Subquery) iExpr() {} +func (ListArg) iExpr() {} +func (*BinaryExpr) iExpr() {} +func (*UnaryExpr) iExpr() {} +func (*IntervalExpr) iExpr() {} +func (*CollateExpr) iExpr() {} +func (*FuncExpr) iExpr() {} +func (*CaseExpr) iExpr() {} +func (*ValuesFuncExpr) iExpr() {} +func (*ConvertExpr) iExpr() {} +func (*MatchExpr) iExpr() {} +func (*GroupConcatExpr) iExpr() {} +func (*Default) iExpr() {} +func (*TimeExpr) iExpr() {} // ReplaceExpr finds the from expression from root // and replaces it with to. If from matches root, @@ -2120,6 +2150,7 @@ const ( HexVal ValArg BitVal + PosArg ) // SQLVal represents a single value. @@ -2163,6 +2194,11 @@ func NewValArg(in []byte) *SQLVal { return &SQLVal{Type: ValArg, Val: in} } +// NewPosArg build a new PosArg. +func NewPosArg(in []byte) *SQLVal { + return &SQLVal{Type: PosArg, Val: in} +} + // Format formats the node. func (node *SQLVal) Format(buf *TrackedBuffer) { switch node.Type { @@ -2176,6 +2212,8 @@ func (node *SQLVal) Format(buf *TrackedBuffer) { buf.Myprintf("B'%s'", []byte(node.Val)) case ValArg: buf.WriteArg(string(node.Val)) + case PosArg: + buf.WriteArg("?") default: panic("unexpected") } diff --git a/vendor/github.com/CovenantSQL/sqlparser/sql.go b/vendor/github.com/CovenantSQL/sqlparser/sql.go index 7a3cde97d..de19d4c40 100644 --- a/vendor/github.com/CovenantSQL/sqlparser/sql.go +++ b/vendor/github.com/CovenantSQL/sqlparser/sql.go @@ -130,97 +130,98 @@ const INTEGRAL = 57384 const FLOAT = 57385 const HEXNUM = 57386 const VALUE_ARG = 57387 -const LIST_ARG = 57388 -const COMMENT = 57389 -const NULL = 57390 -const TRUE = 57391 -const FALSE = 57392 -const FULL = 57393 -const COLUMNS = 57394 -const OR = 57395 -const AND = 57396 -const NOT = 57397 -const BETWEEN = 57398 -const CASE = 57399 -const WHEN = 57400 -const THEN = 57401 -const ELSE = 57402 -const END = 57403 -const LE = 57404 -const GE = 57405 -const NE = 57406 -const IS = 57407 -const LIKE = 57408 -const REGEXP = 57409 -const IN = 57410 -const NULL_SAFE_NOTEQUAL = 57411 -const SHIFT_LEFT = 57412 -const SHIFT_RIGHT = 57413 -const DIV = 57414 -const MOD = 57415 -const UNARY = 57416 -const INTERVAL = 57417 -const CREATE = 57418 -const ALTER = 57419 -const DROP = 57420 -const RENAME = 57421 -const ADD = 57422 -const TABLE = 57423 -const INDEX = 57424 -const TO = 57425 -const IGNORE = 57426 -const IF = 57427 -const UNIQUE = 57428 -const PRIMARY = 57429 -const COLUMN = 57430 -const CONSTRAINT = 57431 -const FOREIGN = 57432 -const SHOW = 57433 -const DESCRIBE = 57434 -const DATE = 57435 -const ESCAPE = 57436 -const EXPLAIN = 57437 -const TINYINT = 57438 -const SMALLINT = 57439 -const MEDIUMINT = 57440 -const INT = 57441 -const INTEGER = 57442 -const BIGINT = 57443 -const INTNUM = 57444 -const REAL = 57445 -const DOUBLE = 57446 -const FLOAT_TYPE = 57447 -const DECIMAL = 57448 -const NUMERIC = 57449 -const TIME = 57450 -const TIMESTAMP = 57451 -const DATETIME = 57452 -const YEAR = 57453 -const CHAR = 57454 -const VARCHAR = 57455 -const BOOL = 57456 -const NCHAR = 57457 -const TEXT = 57458 -const TINYTEXT = 57459 -const MEDIUMTEXT = 57460 -const LONGTEXT = 57461 -const BLOB = 57462 -const TINYBLOB = 57463 -const MEDIUMBLOB = 57464 -const LONGBLOB = 57465 -const AUTO_INCREMENT = 57466 -const SIGNED = 57467 -const UNSIGNED = 57468 -const ZEROFILL = 57469 -const TABLES = 57470 -const CURRENT_TIMESTAMP = 57471 -const CURRENT_DATE = 57472 -const CURRENT_TIME = 57473 -const REPLACE = 57474 -const CAST = 57475 -const GROUP_CONCAT = 57476 -const SEPARATOR = 57477 -const UNUSED = 57478 +const POS_ARG = 57388 +const LIST_ARG = 57389 +const COMMENT = 57390 +const NULL = 57391 +const TRUE = 57392 +const FALSE = 57393 +const FULL = 57394 +const COLUMNS = 57395 +const OR = 57396 +const AND = 57397 +const NOT = 57398 +const BETWEEN = 57399 +const CASE = 57400 +const WHEN = 57401 +const THEN = 57402 +const ELSE = 57403 +const END = 57404 +const LE = 57405 +const GE = 57406 +const NE = 57407 +const IS = 57408 +const LIKE = 57409 +const REGEXP = 57410 +const IN = 57411 +const NULL_SAFE_NOTEQUAL = 57412 +const SHIFT_LEFT = 57413 +const SHIFT_RIGHT = 57414 +const DIV = 57415 +const MOD = 57416 +const UNARY = 57417 +const INTERVAL = 57418 +const CREATE = 57419 +const ALTER = 57420 +const DROP = 57421 +const RENAME = 57422 +const ADD = 57423 +const TABLE = 57424 +const INDEX = 57425 +const TO = 57426 +const IGNORE = 57427 +const IF = 57428 +const UNIQUE = 57429 +const PRIMARY = 57430 +const COLUMN = 57431 +const CONSTRAINT = 57432 +const FOREIGN = 57433 +const SHOW = 57434 +const DESCRIBE = 57435 +const DATE = 57436 +const ESCAPE = 57437 +const EXPLAIN = 57438 +const TINYINT = 57439 +const SMALLINT = 57440 +const MEDIUMINT = 57441 +const INT = 57442 +const INTEGER = 57443 +const BIGINT = 57444 +const INTNUM = 57445 +const REAL = 57446 +const DOUBLE = 57447 +const FLOAT_TYPE = 57448 +const DECIMAL = 57449 +const NUMERIC = 57450 +const TIME = 57451 +const TIMESTAMP = 57452 +const DATETIME = 57453 +const YEAR = 57454 +const CHAR = 57455 +const VARCHAR = 57456 +const BOOL = 57457 +const NCHAR = 57458 +const TEXT = 57459 +const TINYTEXT = 57460 +const MEDIUMTEXT = 57461 +const LONGTEXT = 57462 +const BLOB = 57463 +const TINYBLOB = 57464 +const MEDIUMBLOB = 57465 +const LONGBLOB = 57466 +const AUTO_INCREMENT = 57467 +const SIGNED = 57468 +const UNSIGNED = 57469 +const ZEROFILL = 57470 +const TABLES = 57471 +const CURRENT_TIMESTAMP = 57472 +const CURRENT_DATE = 57473 +const CURRENT_TIME = 57474 +const REPLACE = 57475 +const CAST = 57476 +const GROUP_CONCAT = 57477 +const SEPARATOR = 57478 +const UNUSED = 57479 var yyToknames = [...]string{ "$end", @@ -271,6 +272,7 @@ var yyToknames = [...]string{ "FLOAT", "HEXNUM", "VALUE_ARG", + "POS_ARG", "LIST_ARG", "COMMENT", "NULL", @@ -410,210 +412,206 @@ var yyExca = [...]int{ 38, 157, 40, 157, 41, 157, - 154, 157, + 155, 157, -2, 170, -1, 184, - 92, 355, - -2, 351, + 93, 353, + -2, 349, -1, 185, - 92, 356, - -2, 352, - -1, 402, + 93, 354, + -2, 350, + -1, 400, 5, 17, - -2, 324, - -1, 519, - 92, 358, - -2, 354, - -1, 550, + -2, 322, + -1, 516, + 93, 356, + -2, 352, + -1, 547, 5, 18, - -2, 229, - -1, 607, + -2, 226, + -1, 603, 5, 18, - -2, 325, - -1, 669, + -2, 323, + -1, 665, 5, 17, - -2, 327, - -1, 726, + -2, 325, + -1, 722, 5, 18, - -2, 328, + -2, 326, } const yyPrivate = 57344 -const yyLast = 4100 +const yyLast = 4076 var yyAct = [...]int{ - 185, 621, 533, 338, 382, 429, 172, 534, 514, 459, - 428, 170, 525, 489, 518, 474, 586, 208, 3, 47, - 406, 207, 440, 95, 95, 425, 101, 136, 32, 336, - 45, 545, 118, 121, 405, 464, 434, 430, 187, 95, - 455, 113, 211, 31, 99, 138, 149, 198, 173, 26, - 710, 626, 627, 628, 140, 92, 96, 565, 629, 134, - 111, 447, 625, 124, 702, 151, 700, 681, 466, 112, - 465, 125, 128, 465, 442, 26, 109, 636, 85, 86, - 442, 123, 259, 106, 130, 95, 84, 117, 254, 253, - 95, 559, 516, 91, 714, 639, 354, 122, 357, 358, - 359, 360, 361, 354, 116, 610, 141, 188, 593, 95, - 543, 478, 381, 135, 127, 342, 542, 126, 95, 95, - 95, 664, 132, 526, 190, 95, 448, 100, 95, 496, - 95, 95, 87, 189, 395, 396, 95, 89, 88, 252, - 563, 193, 494, 495, 493, 682, 680, 345, 441, 133, - 261, 194, 215, 630, 441, 26, 684, 251, 439, 438, - 255, 340, 257, 258, 683, 344, 209, 568, 341, 340, - 341, 340, 263, 567, 341, 340, 342, 383, 566, 725, - 388, 666, 623, 116, 342, 444, 342, 250, 343, 526, - 342, 599, 445, 95, 372, 374, 122, 95, 95, 95, - 95, 129, 715, 716, 341, 340, 95, 408, 29, 402, - 95, 139, 373, 95, 677, 676, 397, 47, 492, 380, - 342, 560, 384, 385, 386, 404, 389, 423, 433, 335, - 390, 391, 392, 579, 580, 581, 398, 595, 512, 411, - 403, 413, 407, 471, 139, 117, 117, 117, 117, 634, - 633, 435, 419, 421, 422, 424, 609, 139, 209, 461, - 95, 95, 116, 116, 116, 116, 410, 462, 412, 520, - 139, 196, 139, 341, 340, 116, 352, 362, 363, 355, - 356, 357, 358, 359, 360, 361, 354, 457, 458, 342, - 467, 234, 468, 469, 239, 240, 241, 242, 243, 244, - 721, 245, 246, 247, 248, 249, 235, 236, 237, 238, - 224, 225, 217, 216, 226, 227, 228, 229, 230, 231, - 232, 233, 353, 352, 362, 363, 355, 356, 357, 358, - 359, 360, 361, 354, 482, 484, 485, 720, 631, 47, - 520, 483, 195, 605, 479, 480, 641, 486, 487, 196, - 476, 450, 451, 426, 490, 196, 587, 632, 541, 471, - 553, 25, 452, 453, 454, 25, 353, 352, 362, 363, - 355, 356, 357, 358, 359, 360, 361, 354, 200, 203, - 47, 201, 541, 202, 204, 546, 547, 668, 383, 522, - 33, 513, 472, 433, 29, 569, 393, 188, 29, 29, - 449, 408, 423, 460, 47, 25, 616, 556, 535, 546, - 547, 12, 408, 491, 523, 539, 471, 456, 519, 517, - 41, 537, 471, 26, 418, 203, 400, 401, 521, 47, - 552, 540, 531, 384, 554, 532, 527, 416, 29, 549, - 433, 417, 144, 415, 548, 414, 105, 407, 36, 562, - 536, 719, 26, 718, 551, 200, 203, 557, 201, 463, - 202, 204, 558, 256, 38, 39, 206, 95, 137, 550, - 107, 102, 691, 95, 98, 103, 47, 33, 690, 408, - 638, 426, 260, 528, 529, 530, 131, 476, 119, 35, - 433, 576, 37, 578, 575, 97, 573, 30, 570, 571, - 490, 574, 1, 90, 620, 437, 427, 582, 120, 40, - 436, 679, 624, 443, 572, 519, 517, 564, 446, 561, - 220, 117, 221, 219, 594, 223, 222, 218, 647, 685, - 600, 205, 210, 473, 42, 371, 108, 598, 116, 674, - 383, 538, 394, 186, 689, 637, 613, 597, 387, 524, - 615, 150, 481, 408, 47, 433, 47, 47, 589, 491, - 590, 619, 612, 160, 618, 433, 592, 433, 622, 611, - 95, 157, 159, 601, 602, 603, 604, 158, 617, 147, - 519, 614, 606, 607, 608, 399, 346, 148, 407, 142, - 115, 191, 199, 197, 47, 375, 376, 377, 378, 379, - 640, 645, 635, 665, 544, 433, 114, 470, 591, 709, - 47, 47, 535, 667, 14, 34, 596, 47, 110, 47, - 171, 671, 672, 11, 554, 663, 669, 10, 675, 17, - 433, 646, 9, 8, 7, 6, 47, 644, 673, 5, - 47, 4, 104, 93, 93, 13, 2, 433, 408, 0, - 0, 433, 0, 0, 536, 0, 0, 670, 0, 93, - 0, 698, 0, 0, 0, 25, 27, 15, 16, 0, - 711, 712, 0, 687, 0, 535, 0, 646, 47, 717, - 0, 0, 22, 688, 686, 0, 0, 0, 713, 622, - 0, 694, 0, 695, 722, 383, 723, 0, 29, 0, - 0, 0, 0, 0, 0, 93, 0, 0, 0, 0, - 93, 0, 0, 0, 0, 47, 47, 536, 0, 26, - 0, 0, 0, 0, 0, 0, 727, 728, 0, 93, - 355, 356, 357, 358, 359, 360, 361, 354, 93, 213, - 93, 0, 0, 0, 0, 93, 0, 0, 93, 678, - 93, 93, 18, 19, 20, 724, 262, 339, 0, 726, - 0, 0, 0, 0, 0, 662, 0, 21, 23, 0, - 0, 24, 0, 0, 0, 696, 0, 697, 0, 699, - 0, 701, 0, 703, 704, 705, 706, 707, 708, 488, - 0, 0, 497, 498, 499, 500, 501, 502, 503, 504, - 505, 506, 507, 508, 509, 510, 511, 0, 28, 0, - 0, 0, 0, 93, 0, 0, 409, 93, 93, 93, - 93, 0, 0, 0, 0, 0, 420, 0, 0, 0, - 93, 649, 0, 213, 658, 659, 660, 657, 0, 661, - 0, 0, 0, 0, 651, 0, 654, 656, 650, 0, - 648, 0, 0, 652, 0, 0, 0, 68, 0, 515, - 0, 146, 0, 653, 655, 0, 145, 0, 177, 60, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 29, - 93, 93, 184, 162, 161, 163, 164, 165, 166, 588, - 0, 167, 168, 169, 0, 0, 0, 0, 143, 155, - 0, 176, 0, 0, 0, 0, 0, 0, 0, 353, - 352, 362, 363, 355, 356, 357, 358, 359, 360, 361, - 354, 152, 153, 337, 0, 0, 0, 182, 0, 154, - 0, 156, 0, 0, 0, 0, 0, 583, 584, 585, - 0, 0, 181, 0, 69, 0, 0, 57, 0, 0, - 52, 0, 0, 77, 72, 64, 58, 59, 48, 0, - 70, 55, 56, 54, 67, 74, 75, 53, 82, 51, - 81, 50, 66, 73, 78, 65, 62, 49, 76, 63, - 61, 0, 71, 79, 83, 0, 178, 179, 180, 183, - 174, 175, 0, 80, 0, 0, 0, 0, 0, 0, - 0, 339, 262, 0, 68, 0, 0, 0, 0, 0, - 409, 339, 339, 339, 0, 0, 60, 0, 0, 0, - 0, 409, 0, 0, 348, 0, 351, 0, 0, 46, - 642, 643, 364, 365, 366, 367, 368, 369, 0, 349, - 350, 347, 370, 353, 352, 362, 363, 355, 356, 357, - 358, 359, 360, 361, 354, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 353, 352, 362, 363, 355, 356, - 357, 358, 359, 360, 361, 354, 0, 0, 0, 0, - 0, 0, 0, 0, 693, 0, 0, 93, 409, 0, - 0, 69, 339, 93, 57, 0, 0, 52, 0, 262, + 185, 617, 530, 338, 380, 427, 172, 531, 511, 457, + 32, 170, 522, 487, 515, 583, 148, 208, 3, 47, + 404, 472, 207, 95, 95, 426, 101, 432, 438, 336, + 45, 423, 187, 136, 121, 542, 462, 428, 403, 95, + 453, 113, 211, 31, 99, 138, 706, 140, 173, 26, + 134, 198, 562, 513, 622, 623, 624, 445, 118, 621, + 124, 111, 625, 698, 696, 677, 112, 464, 463, 91, + 125, 128, 463, 109, 440, 26, 632, 85, 86, 123, + 259, 92, 96, 106, 130, 95, 84, 117, 254, 253, + 95, 122, 710, 353, 352, 362, 363, 355, 356, 357, + 358, 359, 360, 361, 354, 635, 141, 188, 87, 95, + 606, 589, 440, 89, 88, 189, 540, 476, 95, 95, + 95, 379, 135, 354, 190, 95, 446, 584, 95, 556, + 95, 95, 342, 127, 539, 660, 95, 340, 523, 523, + 133, 595, 252, 126, 678, 676, 494, 345, 132, 439, + 261, 100, 342, 437, 436, 26, 560, 626, 591, 492, + 493, 491, 344, 263, 194, 29, 209, 193, 355, 356, + 357, 358, 359, 360, 361, 354, 490, 381, 215, 680, + 386, 679, 565, 251, 250, 343, 255, 439, 257, 258, + 335, 564, 563, 95, 372, 341, 340, 95, 95, 95, + 95, 442, 341, 340, 721, 619, 95, 406, 443, 400, + 95, 342, 405, 95, 711, 712, 395, 47, 342, 378, + 129, 122, 382, 383, 384, 557, 387, 421, 431, 460, + 388, 389, 390, 341, 340, 139, 396, 673, 672, 409, + 401, 411, 658, 469, 139, 117, 117, 117, 117, 342, + 717, 402, 433, 419, 420, 422, 417, 716, 209, 459, + 95, 95, 150, 408, 627, 410, 517, 25, 27, 15, + 16, 353, 352, 362, 363, 355, 356, 357, 358, 359, + 360, 361, 354, 601, 22, 393, 394, 455, 456, 630, + 629, 465, 357, 358, 359, 360, 361, 354, 605, 139, + 29, 116, 25, 480, 482, 483, 576, 577, 578, 645, + 481, 196, 654, 655, 656, 653, 470, 657, 466, 467, + 341, 340, 647, 628, 650, 652, 646, 33, 644, 517, + 139, 648, 424, 341, 340, 29, 342, 196, 139, 47, + 662, 649, 651, 538, 477, 478, 469, 484, 485, 342, + 474, 448, 449, 469, 488, 18, 19, 20, 217, 216, + 195, 538, 450, 451, 452, 469, 550, 391, 25, 29, + 21, 23, 25, 196, 24, 447, 200, 203, 47, 201, + 116, 202, 204, 543, 544, 566, 381, 519, 458, 510, + 664, 431, 612, 398, 399, 188, 553, 543, 544, 406, + 421, 29, 47, 454, 524, 29, 532, 516, 371, 41, + 406, 28, 414, 536, 12, 405, 415, 514, 520, 534, + 546, 26, 416, 203, 528, 413, 518, 47, 549, 412, + 559, 382, 551, 715, 548, 537, 714, 529, 431, 144, + 554, 461, 525, 526, 527, 256, 545, 36, 533, 105, + 26, 200, 203, 206, 201, 137, 202, 204, 107, 116, + 116, 116, 116, 38, 39, 95, 555, 547, 102, 687, + 103, 95, 116, 98, 47, 33, 686, 406, 634, 424, + 260, 131, 569, 119, 35, 474, 37, 97, 431, 30, + 1, 575, 90, 572, 616, 573, 435, 567, 488, 571, + 570, 425, 120, 40, 516, 579, 434, 675, 620, 441, + 561, 444, 558, 220, 514, 221, 219, 223, 222, 117, + 218, 590, 643, 568, 681, 205, 210, 596, 471, 42, + 108, 670, 535, 392, 594, 186, 685, 381, 633, 593, + 385, 521, 149, 609, 479, 159, 156, 611, 607, 158, + 406, 47, 431, 47, 47, 405, 157, 586, 615, 608, + 146, 397, 431, 588, 431, 618, 587, 95, 516, 346, + 597, 598, 599, 600, 592, 613, 614, 147, 610, 602, + 603, 604, 142, 115, 191, 199, 197, 541, 114, 468, + 47, 373, 374, 375, 376, 377, 636, 641, 705, 661, + 14, 431, 34, 659, 110, 11, 47, 47, 532, 663, + 489, 10, 17, 47, 9, 47, 171, 667, 668, 8, + 551, 7, 665, 6, 671, 631, 431, 642, 5, 4, + 104, 13, 47, 640, 669, 2, 47, 0, 0, 93, + 93, 0, 0, 431, 406, 0, 0, 431, 0, 684, + 533, 0, 0, 666, 0, 93, 0, 694, 0, 0, + 0, 0, 0, 0, 0, 0, 707, 708, 0, 683, + 0, 532, 0, 642, 47, 713, 0, 0, 0, 0, + 682, 0, 0, 0, 709, 618, 0, 690, 0, 691, + 718, 381, 719, 0, 0, 0, 0, 0, 0, 0, + 0, 93, 0, 0, 0, 0, 93, 0, 0, 0, + 0, 47, 47, 533, 0, 26, 0, 0, 0, 0, + 0, 0, 723, 724, 0, 93, 0, 0, 0, 0, + 0, 0, 0, 116, 93, 213, 93, 637, 0, 0, + 0, 93, 0, 0, 93, 674, 93, 93, 0, 0, + 0, 720, 262, 339, 489, 722, 0, 353, 352, 362, + 363, 355, 356, 357, 358, 359, 360, 361, 354, 0, + 0, 692, 0, 693, 0, 695, 0, 697, 0, 699, + 700, 701, 702, 703, 704, 0, 486, 0, 0, 495, + 496, 497, 498, 499, 500, 501, 502, 503, 504, 505, + 506, 507, 508, 509, 0, 0, 0, 0, 0, 93, + 585, 0, 407, 93, 93, 93, 93, 0, 0, 0, + 0, 0, 418, 0, 0, 0, 93, 0, 0, 213, + 353, 352, 362, 363, 355, 356, 357, 358, 359, 360, + 361, 354, 352, 362, 363, 355, 356, 357, 358, 359, + 360, 361, 354, 68, 0, 512, 0, 145, 0, 0, + 0, 0, 0, 0, 177, 60, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 29, 93, 93, 184, 161, + 160, 162, 163, 164, 165, 166, 0, 0, 167, 168, + 169, 0, 0, 0, 0, 143, 154, 0, 176, 362, + 363, 355, 356, 357, 358, 359, 360, 361, 354, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 151, 152, + 337, 0, 0, 0, 182, 0, 153, 0, 155, 0, + 0, 0, 580, 581, 582, 0, 0, 0, 0, 181, + 0, 69, 0, 0, 57, 0, 0, 52, 0, 0, 77, 72, 64, 58, 59, 48, 0, 70, 55, 56, 54, 67, 74, 75, 53, 82, 51, 81, 50, 66, 73, 78, 65, 62, 49, 76, 63, 61, 0, 71, - 79, 83, 0, 0, 0, 0, 339, 0, 0, 0, - 80, 0, 0, 0, 339, 353, 352, 362, 363, 355, - 356, 357, 358, 359, 360, 361, 354, 0, 0, 0, - 0, 0, 409, 0, 262, 362, 363, 355, 356, 357, - 358, 359, 360, 361, 354, 0, 0, 0, 0, 0, + 79, 83, 0, 178, 179, 180, 183, 174, 175, 0, + 80, 0, 0, 0, 0, 339, 262, 68, 0, 0, + 0, 0, 0, 0, 407, 339, 339, 339, 0, 60, + 0, 0, 0, 0, 0, 407, 0, 0, 0, 0, + 0, 0, 46, 234, 638, 639, 239, 240, 241, 242, + 243, 244, 0, 245, 246, 247, 248, 249, 235, 236, + 237, 238, 224, 225, 0, 0, 226, 227, 228, 229, + 230, 231, 232, 233, 0, 0, 0, 0, 353, 352, + 362, 363, 355, 356, 357, 358, 359, 360, 361, 354, + 0, 0, 0, 0, 0, 0, 0, 689, 0, 0, + 0, 93, 407, 0, 0, 69, 339, 93, 57, 0, + 0, 52, 0, 262, 77, 72, 64, 58, 59, 48, + 0, 70, 55, 56, 54, 67, 74, 75, 53, 82, + 51, 81, 50, 66, 73, 78, 65, 62, 49, 76, + 63, 61, 0, 71, 79, 83, 0, 0, 0, 339, + 0, 0, 0, 0, 80, 0, 0, 339, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 93, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 407, 0, 262, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 339, 327, 318, 297, 329, 278, 289, - 333, 290, 291, 312, 270, 305, 68, 0, 281, 266, - 287, 267, 279, 299, 302, 277, 320, 331, 60, 301, - 303, 317, 296, 313, 273, 307, 310, 330, 0, 0, - 0, 46, 0, 431, 432, 0, 0, 409, 0, 0, - 309, 326, 288, 0, 0, 311, 265, 308, 0, 269, - 271, 332, 324, 284, 285, 555, 0, 0, 0, 0, - 0, 300, 304, 314, 294, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 282, 0, 306, 0, 0, 0, - 298, 0, 272, 0, 283, 315, 264, 322, 295, 325, - 293, 292, 328, 69, 0, 0, 57, 321, 280, 52, - 286, 334, 77, 72, 64, 58, 59, 48, 0, 70, - 55, 56, 54, 67, 74, 75, 53, 82, 51, 81, - 50, 66, 73, 78, 65, 62, 49, 76, 63, 61, - 268, 71, 79, 83, 323, 276, 274, 275, 316, 0, - 0, 319, 80, 327, 318, 297, 329, 278, 289, 333, - 290, 291, 312, 270, 305, 68, 0, 281, 266, 287, - 267, 279, 299, 302, 277, 320, 331, 60, 301, 303, - 317, 296, 313, 273, 307, 310, 330, 0, 0, 0, - 46, 0, 431, 432, 0, 0, 0, 0, 0, 309, - 326, 288, 0, 0, 311, 265, 308, 0, 269, 271, - 332, 324, 284, 285, 0, 0, 0, 0, 0, 0, - 300, 304, 314, 294, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 282, 0, 306, 0, 0, 0, 298, - 0, 272, 0, 283, 315, 264, 322, 295, 325, 293, - 292, 328, 69, 0, 0, 57, 321, 280, 52, 286, - 334, 77, 72, 64, 58, 59, 48, 0, 70, 55, - 56, 54, 67, 74, 75, 53, 82, 51, 81, 50, - 66, 73, 78, 65, 62, 49, 76, 63, 61, 268, - 71, 79, 83, 323, 276, 274, 275, 316, 0, 0, - 319, 80, 327, 318, 297, 329, 278, 289, 333, 290, - 291, 312, 270, 305, 68, 0, 281, 266, 287, 267, - 279, 299, 302, 277, 320, 331, 60, 301, 303, 317, - 296, 313, 273, 307, 310, 330, 0, 0, 0, 46, - 0, 0, 0, 0, 0, 0, 0, 0, 309, 326, - 288, 0, 0, 311, 265, 308, 0, 269, 271, 332, - 324, 284, 285, 0, 0, 0, 0, 0, 0, 300, - 304, 314, 294, 0, 0, 0, 0, 0, 0, 0, - 692, 0, 282, 0, 306, 0, 0, 0, 298, 0, - 272, 0, 283, 315, 264, 322, 295, 325, 293, 292, - 328, 69, 0, 0, 57, 321, 280, 52, 286, 334, - 77, 72, 64, 58, 59, 48, 0, 70, 55, 56, - 54, 67, 74, 75, 53, 82, 51, 81, 50, 66, - 73, 78, 65, 62, 49, 76, 63, 61, 268, 71, - 79, 83, 323, 276, 274, 275, 316, 0, 0, 319, - 80, 327, 318, 297, 329, 278, 289, 333, 290, 291, - 312, 270, 305, 68, 0, 281, 266, 287, 267, 279, - 299, 302, 277, 320, 331, 60, 301, 303, 317, 296, - 313, 273, 307, 310, 330, 29, 0, 0, 46, 0, + 327, 318, 297, 329, 278, 289, 333, 290, 291, 312, + 270, 305, 68, 93, 281, 266, 287, 267, 279, 299, + 302, 277, 320, 331, 60, 301, 303, 317, 296, 313, + 273, 307, 310, 330, 0, 339, 0, 46, 0, 429, + 430, 0, 0, 0, 0, 0, 0, 309, 326, 288, + 0, 0, 311, 265, 308, 0, 269, 271, 332, 324, + 284, 285, 552, 0, 0, 0, 0, 0, 300, 304, + 314, 294, 0, 0, 0, 0, 0, 0, 0, 407, + 0, 282, 0, 306, 0, 0, 0, 298, 0, 272, + 0, 283, 315, 264, 322, 295, 325, 293, 292, 328, + 69, 0, 0, 57, 321, 280, 52, 286, 334, 77, + 72, 64, 58, 59, 48, 0, 70, 55, 56, 54, + 67, 74, 75, 53, 82, 51, 81, 50, 66, 73, + 78, 65, 62, 49, 76, 63, 61, 268, 71, 79, + 83, 323, 276, 274, 275, 316, 0, 0, 319, 80, + 327, 318, 297, 329, 278, 289, 333, 290, 291, 312, + 270, 305, 68, 0, 281, 266, 287, 267, 279, 299, + 302, 277, 320, 331, 60, 301, 303, 317, 296, 313, + 273, 307, 310, 330, 0, 0, 0, 46, 0, 429, + 430, 0, 0, 0, 0, 0, 0, 309, 326, 288, + 0, 0, 311, 265, 308, 0, 269, 271, 332, 324, + 284, 285, 0, 0, 0, 0, 0, 0, 300, 304, + 314, 294, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 282, 0, 306, 0, 0, 0, 298, 0, 272, + 0, 283, 315, 264, 322, 295, 325, 293, 292, 328, + 69, 0, 0, 57, 321, 280, 52, 286, 334, 77, + 72, 64, 58, 59, 48, 0, 70, 55, 56, 54, + 67, 74, 75, 53, 82, 51, 81, 50, 66, 73, + 78, 65, 62, 49, 76, 63, 61, 268, 71, 79, + 83, 323, 276, 274, 275, 316, 0, 0, 319, 80, + 327, 318, 297, 329, 278, 289, 333, 290, 291, 312, + 270, 305, 68, 0, 281, 266, 287, 267, 279, 299, + 302, 277, 320, 331, 60, 301, 303, 317, 296, 313, + 273, 307, 310, 330, 0, 0, 0, 46, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 309, 326, 288, + 0, 0, 311, 265, 308, 0, 269, 271, 332, 324, + 284, 285, 0, 0, 0, 0, 0, 0, 300, 304, + 314, 294, 0, 0, 0, 0, 0, 0, 0, 688, + 0, 282, 0, 306, 0, 0, 0, 298, 0, 272, + 0, 283, 315, 264, 322, 295, 325, 293, 292, 328, + 69, 0, 0, 57, 321, 280, 52, 286, 334, 77, + 72, 64, 58, 59, 48, 0, 70, 55, 56, 54, + 67, 74, 75, 53, 82, 51, 81, 50, 66, 73, + 78, 65, 62, 49, 76, 63, 61, 268, 71, 79, + 83, 323, 276, 274, 275, 316, 0, 0, 319, 80, + 327, 318, 297, 329, 278, 289, 333, 290, 291, 312, + 270, 305, 68, 0, 281, 266, 287, 267, 279, 299, + 302, 277, 320, 331, 60, 301, 303, 317, 296, 313, + 273, 307, 310, 330, 29, 0, 0, 46, 0, 0, 0, 0, 0, 0, 0, 0, 0, 309, 326, 288, 0, 0, 311, 265, 308, 0, 269, 271, 332, 324, 284, 285, 0, 0, 0, 0, 0, 0, 300, 304, @@ -629,131 +627,132 @@ var yyAct = [...]int{ 270, 305, 68, 0, 281, 266, 287, 267, 279, 299, 302, 277, 320, 331, 60, 301, 303, 317, 296, 313, 273, 307, 310, 330, 0, 0, 0, 184, 0, 0, - 0, 0, 0, 0, 0, 0, 309, 326, 288, 0, - 0, 311, 265, 308, 0, 269, 271, 332, 324, 284, - 285, 0, 0, 0, 0, 0, 0, 300, 304, 314, - 294, 0, 0, 0, 0, 0, 0, 0, 577, 0, - 282, 0, 306, 0, 0, 0, 298, 0, 272, 0, - 283, 315, 264, 322, 295, 325, 293, 292, 328, 69, - 0, 0, 57, 321, 280, 52, 286, 334, 77, 72, - 64, 58, 59, 48, 0, 70, 55, 56, 54, 67, - 74, 75, 53, 82, 51, 81, 50, 66, 73, 78, - 65, 62, 49, 76, 63, 61, 268, 71, 79, 83, - 323, 276, 274, 275, 316, 0, 0, 319, 80, 327, - 318, 297, 329, 278, 289, 333, 290, 291, 312, 270, - 305, 68, 0, 281, 266, 287, 267, 279, 299, 302, - 277, 320, 331, 60, 301, 303, 317, 296, 313, 273, - 307, 310, 330, 0, 0, 0, 46, 0, 0, 0, - 0, 0, 0, 0, 0, 309, 326, 288, 0, 0, - 311, 265, 308, 0, 269, 271, 332, 324, 284, 285, - 0, 0, 0, 0, 0, 0, 300, 304, 314, 294, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 282, - 0, 306, 0, 0, 0, 298, 0, 272, 0, 283, - 315, 264, 322, 295, 325, 293, 292, 328, 69, 0, - 0, 57, 321, 280, 52, 286, 334, 77, 72, 64, - 58, 59, 48, 0, 70, 55, 56, 54, 67, 74, - 75, 53, 82, 51, 81, 50, 66, 73, 78, 65, - 62, 49, 76, 63, 61, 268, 71, 79, 83, 323, - 276, 274, 275, 316, 0, 0, 319, 80, 327, 318, - 297, 329, 278, 289, 333, 290, 291, 312, 270, 305, - 68, 0, 281, 266, 287, 267, 279, 299, 302, 277, - 320, 331, 60, 301, 303, 317, 296, 313, 273, 307, - 310, 330, 0, 0, 0, 184, 0, 0, 0, 0, - 0, 0, 0, 0, 309, 326, 288, 0, 0, 311, - 265, 308, 0, 269, 271, 332, 324, 284, 285, 0, - 0, 0, 0, 0, 0, 300, 304, 314, 294, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 282, 0, - 306, 0, 0, 0, 298, 0, 272, 0, 283, 315, - 264, 322, 295, 325, 293, 292, 328, 69, 0, 0, - 57, 321, 280, 52, 286, 334, 77, 72, 64, 58, - 59, 48, 0, 70, 55, 56, 54, 67, 74, 75, - 53, 82, 51, 81, 50, 66, 73, 78, 65, 62, - 49, 76, 63, 61, 268, 71, 79, 83, 323, 276, - 274, 275, 316, 0, 0, 319, 80, 327, 318, 297, - 329, 278, 289, 333, 290, 291, 312, 270, 305, 68, - 0, 281, 266, 287, 267, 279, 299, 302, 277, 320, - 331, 60, 301, 303, 317, 296, 313, 273, 307, 310, - 330, 0, 0, 0, 94, 0, 0, 0, 0, 0, - 0, 0, 0, 309, 326, 288, 0, 0, 311, 265, - 308, 0, 269, 271, 332, 324, 284, 285, 0, 0, - 0, 0, 0, 0, 300, 304, 314, 294, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 282, 0, 306, - 0, 0, 0, 298, 0, 272, 0, 283, 315, 264, - 322, 295, 325, 293, 292, 328, 69, 0, 0, 57, - 321, 280, 52, 286, 334, 77, 72, 64, 58, 59, - 48, 0, 70, 55, 56, 54, 67, 74, 75, 53, - 82, 51, 81, 50, 66, 73, 78, 65, 62, 49, - 76, 63, 61, 268, 71, 79, 83, 323, 276, 274, - 275, 316, 68, 0, 319, 80, 146, 0, 0, 0, - 0, 145, 0, 177, 60, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 29, 0, 0, 184, 162, 161, - 163, 164, 165, 166, 0, 0, 167, 168, 169, 0, - 0, 0, 0, 143, 155, 0, 176, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 152, 153, 337, 0, - 0, 0, 182, 0, 154, 0, 156, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 181, 0, 69, - 0, 0, 57, 0, 0, 52, 0, 0, 77, 72, - 64, 58, 59, 48, 0, 70, 55, 56, 54, 67, - 74, 75, 53, 82, 51, 81, 50, 66, 73, 78, - 65, 62, 49, 76, 63, 61, 0, 71, 79, 83, - 0, 178, 179, 180, 183, 174, 175, 68, 80, 0, - 0, 146, 0, 0, 0, 0, 145, 0, 177, 60, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 29, - 0, 139, 184, 162, 161, 163, 164, 165, 166, 0, - 0, 167, 168, 169, 0, 0, 0, 0, 143, 155, + 0, 0, 0, 0, 0, 0, 0, 309, 326, 288, + 0, 0, 311, 265, 308, 0, 269, 271, 332, 324, + 284, 285, 0, 0, 0, 0, 0, 0, 300, 304, + 314, 294, 0, 0, 0, 0, 0, 0, 0, 574, + 0, 282, 0, 306, 0, 0, 0, 298, 0, 272, + 0, 283, 315, 264, 322, 295, 325, 293, 292, 328, + 69, 0, 0, 57, 321, 280, 52, 286, 334, 77, + 72, 64, 58, 59, 48, 0, 70, 55, 56, 54, + 67, 74, 75, 53, 82, 51, 81, 50, 66, 73, + 78, 65, 62, 49, 76, 63, 61, 268, 71, 79, + 83, 323, 276, 274, 275, 316, 0, 0, 319, 80, + 327, 318, 297, 329, 278, 289, 333, 290, 291, 312, + 270, 305, 68, 0, 281, 266, 287, 267, 279, 299, + 302, 277, 320, 331, 60, 301, 303, 317, 296, 313, + 273, 307, 310, 330, 0, 0, 0, 46, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 309, 326, 288, + 0, 0, 311, 265, 308, 0, 269, 271, 332, 324, + 284, 285, 0, 0, 0, 0, 0, 0, 300, 304, + 314, 294, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 282, 0, 306, 0, 0, 0, 298, 0, 272, + 0, 283, 315, 264, 322, 295, 325, 293, 292, 328, + 69, 0, 0, 57, 321, 280, 52, 286, 334, 77, + 72, 64, 58, 59, 48, 0, 70, 55, 56, 54, + 67, 74, 75, 53, 82, 51, 81, 50, 66, 73, + 78, 65, 62, 49, 76, 63, 61, 268, 71, 79, + 83, 323, 276, 274, 275, 316, 0, 0, 319, 80, + 327, 318, 297, 329, 278, 289, 333, 290, 291, 312, + 270, 305, 68, 0, 281, 266, 287, 267, 279, 299, + 302, 277, 320, 331, 60, 301, 303, 317, 296, 313, + 273, 307, 310, 330, 0, 0, 0, 184, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 309, 326, 288, + 0, 0, 311, 265, 308, 0, 269, 271, 332, 324, + 284, 285, 0, 0, 0, 0, 0, 0, 300, 304, + 314, 294, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 282, 0, 306, 0, 0, 0, 298, 0, 272, + 0, 283, 315, 264, 322, 295, 325, 293, 292, 328, + 69, 0, 0, 57, 321, 280, 52, 286, 334, 77, + 72, 64, 58, 59, 48, 0, 70, 55, 56, 54, + 67, 74, 75, 53, 82, 51, 81, 50, 66, 73, + 78, 65, 62, 49, 76, 63, 61, 268, 71, 79, + 83, 323, 276, 274, 275, 316, 0, 0, 319, 80, + 327, 318, 297, 329, 278, 289, 333, 290, 291, 312, + 270, 305, 68, 0, 281, 266, 287, 267, 279, 299, + 302, 277, 320, 331, 60, 301, 303, 317, 296, 313, + 273, 307, 310, 330, 0, 0, 0, 94, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 309, 326, 288, + 0, 0, 311, 265, 308, 0, 269, 271, 332, 324, + 284, 285, 0, 0, 0, 0, 0, 0, 300, 304, + 314, 294, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 282, 0, 306, 0, 0, 0, 298, 0, 272, + 0, 283, 315, 264, 322, 295, 325, 293, 292, 328, + 69, 0, 0, 57, 321, 280, 52, 286, 334, 77, + 72, 64, 58, 59, 48, 0, 70, 55, 56, 54, + 67, 74, 75, 53, 82, 51, 81, 50, 66, 73, + 78, 65, 62, 49, 76, 63, 61, 268, 71, 79, + 83, 323, 276, 274, 275, 316, 68, 0, 319, 80, + 145, 0, 0, 0, 0, 0, 0, 177, 60, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 29, 0, + 0, 184, 161, 160, 162, 163, 164, 165, 166, 0, + 0, 167, 168, 169, 0, 0, 0, 0, 143, 154, 0, 176, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 152, 153, 0, 0, 0, 0, 182, 0, 154, - 0, 156, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 151, 152, 337, 0, 0, 0, 182, 0, 153, + 0, 155, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 181, 0, 69, 0, 0, 57, 0, 0, 52, 0, 0, 77, 72, 64, 58, 59, 48, 0, 70, 55, 56, 54, 67, 74, 75, 53, 82, 51, 81, 50, 66, 73, 78, 65, 62, 49, 76, 63, - 61, 25, 71, 79, 83, 0, 178, 179, 180, 183, - 174, 175, 68, 80, 0, 0, 146, 0, 0, 0, - 0, 145, 0, 177, 60, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 29, 0, 0, 184, 162, 161, - 163, 164, 165, 166, 0, 0, 167, 168, 169, 0, - 0, 0, 0, 143, 155, 0, 176, 0, 0, 0, + 61, 0, 71, 79, 83, 0, 178, 179, 180, 183, + 174, 175, 68, 80, 0, 0, 145, 0, 0, 0, + 0, 0, 0, 177, 60, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 29, 0, 139, 184, 161, 160, + 162, 163, 164, 165, 166, 0, 0, 167, 168, 169, + 0, 0, 0, 0, 143, 154, 0, 176, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 152, 153, 0, 0, - 0, 0, 182, 0, 154, 0, 156, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 181, 0, 69, - 0, 0, 57, 0, 0, 52, 0, 0, 77, 72, - 64, 58, 59, 48, 0, 70, 55, 56, 54, 67, - 74, 75, 53, 82, 51, 81, 50, 66, 73, 78, - 65, 62, 49, 76, 63, 61, 0, 71, 79, 83, - 0, 178, 179, 180, 183, 174, 175, 68, 80, 0, - 0, 146, 0, 0, 0, 0, 145, 0, 177, 60, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 29, - 0, 0, 184, 162, 161, 163, 164, 165, 166, 0, - 0, 167, 168, 169, 0, 0, 0, 0, 143, 155, - 0, 176, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 151, 152, 0, + 0, 0, 0, 182, 0, 153, 0, 155, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 181, 0, + 69, 0, 0, 57, 0, 0, 52, 0, 0, 77, + 72, 64, 58, 59, 48, 0, 70, 55, 56, 54, + 67, 74, 75, 53, 82, 51, 81, 50, 66, 73, + 78, 65, 62, 49, 76, 63, 61, 25, 71, 79, + 83, 0, 178, 179, 180, 183, 174, 175, 68, 80, + 0, 0, 145, 0, 0, 0, 0, 0, 0, 177, + 60, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 29, 0, 0, 184, 161, 160, 162, 163, 164, 165, + 166, 0, 0, 167, 168, 169, 0, 0, 0, 0, + 143, 154, 0, 176, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 152, 153, 0, 0, 0, 0, 182, 0, 154, - 0, 156, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 181, 0, 69, 0, 0, 57, 0, 0, - 52, 0, 0, 77, 72, 64, 58, 59, 48, 0, - 70, 55, 56, 54, 67, 74, 75, 53, 82, 51, - 81, 50, 66, 73, 78, 65, 62, 49, 76, 63, - 61, 0, 71, 79, 83, 68, 178, 179, 180, 183, - 174, 175, 0, 80, 0, 0, 177, 60, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 29, 0, 0, - 184, 162, 161, 163, 164, 165, 166, 0, 0, 167, - 168, 169, 0, 0, 0, 0, 0, 155, 0, 176, + 0, 0, 0, 151, 152, 0, 0, 0, 0, 182, + 0, 153, 0, 155, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 181, 0, 69, 0, 0, 57, + 0, 0, 52, 0, 0, 77, 72, 64, 58, 59, + 48, 0, 70, 55, 56, 54, 67, 74, 75, 53, + 82, 51, 81, 50, 66, 73, 78, 65, 62, 49, + 76, 63, 61, 0, 71, 79, 83, 0, 178, 179, + 180, 183, 174, 175, 68, 80, 0, 0, 145, 0, + 0, 0, 0, 0, 0, 177, 60, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 29, 0, 0, 184, + 161, 160, 162, 163, 164, 165, 166, 0, 0, 167, + 168, 169, 0, 0, 0, 0, 143, 154, 0, 176, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 152, - 153, 0, 0, 0, 0, 182, 0, 154, 0, 156, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 151, + 152, 0, 0, 0, 0, 182, 0, 153, 0, 155, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 181, 0, 69, 0, 0, 57, 0, 0, 52, 0, 0, 77, 72, 64, 58, 59, 48, 0, 70, 55, 56, 54, 67, 74, 75, 53, 82, 51, 81, 50, 66, 73, 78, 65, 62, 49, 76, 63, 61, 0, - 71, 79, 83, 0, 178, 179, 180, 183, 174, 175, - 68, 80, 0, 475, 0, 0, 0, 0, 0, 0, - 0, 0, 60, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 46, 0, 477, 0, 0, + 71, 79, 83, 68, 178, 179, 180, 183, 174, 175, + 0, 80, 0, 0, 177, 60, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 29, 0, 0, 184, 161, + 160, 162, 163, 164, 165, 166, 0, 0, 167, 168, + 169, 0, 0, 0, 0, 0, 154, 0, 176, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 151, 152, + 0, 0, 0, 0, 182, 0, 153, 0, 155, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 181, + 0, 69, 0, 0, 57, 0, 0, 52, 0, 0, + 77, 72, 64, 58, 59, 48, 0, 70, 55, 56, + 54, 67, 74, 75, 53, 82, 51, 81, 50, 66, + 73, 78, 65, 62, 49, 76, 63, 61, 0, 71, + 79, 83, 0, 178, 179, 180, 183, 174, 175, 68, + 80, 0, 473, 0, 0, 0, 0, 0, 0, 0, + 0, 60, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 46, 0, 475, 0, 0, 0, 25, 0, 0, 0, 0, 0, 0, 0, 0, 341, 340, 68, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 60, 0, 342, 0, 0, 0, 0, @@ -763,240 +762,240 @@ var yyAct = [...]int{ 57, 0, 0, 52, 0, 0, 77, 72, 64, 58, 59, 48, 0, 70, 55, 56, 54, 67, 74, 75, 53, 82, 51, 81, 50, 66, 73, 78, 65, 62, - 49, 76, 63, 61, 0, 71, 79, 83, 69, 0, - 0, 57, 0, 0, 52, 0, 80, 77, 72, 64, - 58, 59, 48, 0, 70, 55, 56, 54, 67, 74, - 75, 53, 82, 51, 81, 50, 66, 73, 78, 65, - 62, 49, 76, 63, 61, 25, 71, 79, 83, 0, - 0, 0, 0, 0, 0, 0, 68, 80, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 60, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 29, 0, - 0, 94, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 68, 0, 0, - 212, 0, 0, 0, 0, 0, 0, 0, 0, 60, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 94, 0, 214, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 69, 0, 0, 57, 0, 0, 52, - 0, 0, 77, 72, 64, 58, 59, 48, 0, 70, - 55, 56, 54, 67, 74, 75, 53, 82, 51, 81, - 50, 66, 73, 78, 65, 62, 49, 76, 63, 61, - 0, 71, 79, 83, 69, 0, 0, 57, 0, 0, - 52, 0, 80, 77, 72, 64, 58, 59, 48, 0, - 70, 55, 56, 54, 67, 74, 75, 53, 82, 51, - 81, 50, 66, 73, 78, 65, 62, 49, 76, 63, - 61, 0, 71, 79, 83, 68, 0, 0, 0, 0, - 0, 0, 0, 80, 0, 0, 0, 60, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 46, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 68, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 60, 0, + 49, 76, 63, 61, 0, 71, 79, 83, 0, 69, + 0, 0, 57, 0, 0, 52, 80, 0, 77, 72, + 64, 58, 59, 48, 0, 70, 55, 56, 54, 67, + 74, 75, 53, 82, 51, 81, 50, 66, 73, 78, + 65, 62, 49, 76, 63, 61, 25, 71, 79, 83, + 0, 0, 0, 0, 0, 0, 0, 68, 80, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 60, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 29, + 0, 0, 94, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 68, 0, + 0, 212, 0, 0, 0, 0, 0, 0, 0, 0, + 60, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 94, 0, 214, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 46, 0, 477, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 43, 0, 0, 0, - 0, 44, 69, 0, 0, 57, 0, 0, 52, 0, - 0, 77, 72, 64, 58, 59, 48, 0, 70, 55, - 56, 54, 67, 74, 75, 53, 82, 51, 81, 50, - 66, 73, 78, 65, 62, 49, 76, 63, 61, 0, - 71, 79, 83, 69, 0, 0, 57, 0, 0, 52, - 0, 80, 77, 72, 64, 58, 59, 48, 0, 70, - 55, 56, 54, 67, 74, 75, 53, 82, 51, 81, - 50, 66, 73, 78, 65, 62, 49, 76, 63, 61, - 0, 71, 79, 83, 68, 0, 0, 0, 0, 0, - 0, 0, 80, 0, 0, 0, 60, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 29, 0, 0, 94, + 0, 0, 0, 0, 0, 69, 0, 0, 57, 0, + 0, 52, 0, 0, 77, 72, 64, 58, 59, 48, + 0, 70, 55, 56, 54, 67, 74, 75, 53, 82, + 51, 81, 50, 66, 73, 78, 65, 62, 49, 76, + 63, 61, 0, 71, 79, 83, 69, 0, 0, 57, + 0, 0, 52, 0, 80, 77, 72, 64, 58, 59, + 48, 0, 70, 55, 56, 54, 67, 74, 75, 53, + 82, 51, 81, 50, 66, 73, 78, 65, 62, 49, + 76, 63, 61, 0, 71, 79, 83, 68, 0, 0, + 0, 0, 0, 0, 0, 80, 0, 0, 0, 60, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 68, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 60, 0, 0, + 0, 0, 46, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 68, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 94, 0, 214, 0, 0, 0, 0, 0, 0, 0, + 60, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 46, 0, 475, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 43, + 0, 0, 0, 0, 44, 69, 0, 0, 57, 0, + 0, 52, 0, 0, 77, 72, 64, 58, 59, 48, + 0, 70, 55, 56, 54, 67, 74, 75, 53, 82, + 51, 81, 50, 66, 73, 78, 65, 62, 49, 76, + 63, 61, 0, 71, 79, 83, 69, 0, 0, 57, + 0, 0, 52, 0, 80, 77, 72, 64, 58, 59, + 48, 0, 70, 55, 56, 54, 67, 74, 75, 53, + 82, 51, 81, 50, 66, 73, 78, 65, 62, 49, + 76, 63, 61, 0, 71, 79, 83, 68, 0, 0, + 0, 0, 0, 0, 0, 80, 0, 0, 0, 60, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 29, + 0, 0, 94, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 68, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 69, 0, 0, 57, 0, 0, 52, 0, 0, - 77, 72, 64, 58, 59, 48, 0, 70, 55, 56, - 54, 67, 74, 75, 53, 82, 51, 81, 50, 66, - 73, 78, 65, 62, 49, 76, 63, 61, 0, 71, - 79, 83, 69, 0, 0, 57, 0, 0, 52, 0, - 80, 77, 72, 64, 58, 59, 48, 0, 70, 55, - 56, 54, 67, 74, 75, 53, 82, 51, 81, 50, - 66, 73, 78, 65, 62, 49, 76, 63, 61, 0, - 71, 79, 83, 68, 0, 0, 0, 0, 0, 0, - 192, 80, 0, 0, 0, 60, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 94, 0, + 60, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 94, 0, 214, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 68, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 60, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 46, + 0, 0, 0, 0, 0, 69, 0, 0, 57, 0, + 0, 52, 0, 0, 77, 72, 64, 58, 59, 48, + 0, 70, 55, 56, 54, 67, 74, 75, 53, 82, + 51, 81, 50, 66, 73, 78, 65, 62, 49, 76, + 63, 61, 0, 71, 79, 83, 69, 0, 0, 57, + 0, 0, 52, 0, 80, 77, 72, 64, 58, 59, + 48, 0, 70, 55, 56, 54, 67, 74, 75, 53, + 82, 51, 81, 50, 66, 73, 78, 65, 62, 49, + 76, 63, 61, 0, 71, 79, 83, 68, 0, 0, + 0, 0, 0, 0, 192, 80, 0, 0, 0, 60, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 94, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 68, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 348, + 60, 351, 0, 0, 0, 0, 0, 364, 365, 366, + 367, 368, 369, 46, 349, 350, 347, 370, 353, 352, + 362, 363, 355, 356, 357, 358, 359, 360, 361, 354, + 0, 0, 0, 0, 0, 69, 0, 0, 57, 0, + 0, 52, 0, 0, 77, 72, 64, 58, 59, 48, + 0, 70, 55, 56, 54, 67, 74, 75, 53, 82, + 51, 81, 50, 66, 73, 78, 65, 62, 49, 76, + 63, 61, 0, 71, 79, 83, 69, 0, 0, 57, + 0, 0, 52, 0, 80, 77, 72, 64, 58, 59, + 48, 0, 70, 55, 56, 54, 67, 74, 75, 53, + 82, 51, 81, 50, 66, 73, 78, 65, 62, 49, + 76, 63, 61, 0, 71, 79, 83, 68, 0, 0, + 0, 0, 0, 0, 0, 80, 0, 0, 0, 60, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 69, 0, 0, 57, 0, 0, 52, 0, 0, 77, - 72, 64, 58, 59, 48, 0, 70, 55, 56, 54, - 67, 74, 75, 53, 82, 51, 81, 50, 66, 73, - 78, 65, 62, 49, 76, 63, 61, 0, 71, 79, - 83, 69, 0, 0, 57, 0, 0, 52, 0, 80, - 77, 72, 64, 58, 59, 48, 0, 70, 55, 56, - 54, 67, 74, 75, 53, 82, 51, 81, 50, 66, - 73, 78, 65, 62, 49, 76, 63, 61, 0, 71, - 79, 83, 68, 0, 0, 0, 0, 0, 0, 0, - 80, 0, 0, 0, 60, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 184, 0, 0, + 0, 0, 184, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 68, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 68, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 60, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 94, 0, + 60, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 94, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 69, - 0, 0, 57, 0, 0, 52, 0, 0, 77, 72, - 64, 58, 59, 48, 0, 70, 55, 56, 54, 67, - 74, 75, 53, 82, 51, 81, 50, 66, 73, 78, - 65, 62, 49, 76, 63, 61, 0, 71, 79, 83, - 69, 0, 0, 57, 0, 0, 52, 0, 80, 77, - 72, 64, 58, 59, 48, 0, 70, 55, 56, 54, - 67, 74, 75, 53, 82, 51, 81, 50, 66, 73, - 78, 65, 62, 49, 76, 63, 61, 0, 71, 79, - 83, 0, 0, 0, 0, 0, 0, 0, 0, 80, + 0, 0, 0, 0, 0, 69, 0, 0, 57, 0, + 0, 52, 0, 0, 77, 72, 64, 58, 59, 48, + 0, 70, 55, 56, 54, 67, 74, 75, 53, 82, + 51, 81, 50, 66, 73, 78, 65, 62, 49, 76, + 63, 61, 0, 71, 79, 83, 69, 0, 0, 57, + 0, 0, 52, 0, 80, 77, 72, 64, 58, 59, + 48, 0, 70, 55, 56, 54, 67, 74, 75, 53, + 82, 51, 81, 50, 66, 73, 78, 65, 62, 49, + 76, 63, 61, 0, 71, 79, 83, 0, 0, 0, + 0, 0, 0, 0, 0, 80, } var yyPact = [...]int{ - 659, -1000, -111, -1000, -1000, -1000, -1000, -1000, -1000, -1000, - -1000, -1000, 463, 484, -1000, -1000, -1000, 381, 3398, -12, - -20, 39, 3946, 3946, -1000, -1000, 355, 71, -1000, -1000, - -1000, -1000, 455, 460, 355, 452, -25, -1000, 3567, 478, - -1000, 154, -18, -31, -1000, -1000, -1000, -1000, -1000, -1000, + 261, -1000, -112, -1000, -1000, -1000, -1000, -1000, -1000, -1000, + -1000, -1000, 461, 479, -1000, -1000, -1000, 370, 3370, -13, + -22, 14, 3921, 3921, -1000, -1000, 296, 94, -1000, -1000, + -1000, -1000, 452, 455, 296, 440, -29, -1000, 3540, 473, + -1000, 179, -21, -33, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, - -1000, -1000, -1000, -1000, 3946, -30, -30, -14, 476, 3946, - 4, -1000, -1000, 21, -1000, -1000, -1000, -1000, 449, 170, - -95, -1000, 2790, 2790, 463, -1000, 355, -1000, 3736, -1000, - 101, 315, 425, -1000, -1000, -1000, 446, 3229, 3270, 3946, - 272, -1000, 181, -31, 3946, 81, -8, 3946, 442, 3946, - 3946, -16, -1000, -1000, 472, 2252, 2385, -1000, -1000, -1000, - -1000, 148, -1000, 2790, 966, 360, 360, -1000, -1000, -1000, - -1000, -1000, 2918, 2918, 2918, 2918, 2918, -1000, -1000, -1000, + -1000, -1000, -1000, -1000, 3921, -32, -32, -15, 471, 3921, + -6, -1000, -1000, 29, -1000, -1000, -1000, -1000, 436, 194, + -103, -1000, 2757, 2757, 461, -1000, 296, -1000, 3710, -1000, + 113, 333, 421, -1000, -1000, -1000, 433, 3200, 3241, 3921, + 318, -1000, 912, -33, 3921, 83, -9, 3921, 424, 3921, + 3921, -19, -1000, -1000, 470, 2215, 2349, -1000, -1000, -1000, + -1000, 145, -1000, 2757, 3720, 330, -1000, -1000, -1000, -1000, + -1000, 2886, 2886, 2886, 2886, 2886, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, - 360, 20, -1000, 2655, 360, 360, 2790, 360, -1000, -1000, - -1000, 360, 360, 360, -1000, -1000, 356, -1000, 112, 455, - 170, 399, 3946, -1000, -1000, 3905, 3567, 3567, 3567, 3567, - -1000, 415, 413, 407, 394, 3946, -1000, 231, 170, 3229, - -1000, -1000, 3608, -1000, -1000, 470, 1358, 55, 134, -82, - -1000, -1000, 361, -1000, 361, 361, -1000, -1000, -1000, -1000, - -1000, -1000, -1000, -1000, -1000, 361, 361, 361, -1000, -1000, - -1000, -1000, -1000, -1000, -1000, 378, 378, 378, 364, 364, - 225, -1000, 438, -35, -32, -1000, -1000, -1000, -1000, 3946, - 3946, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, + 330, 28, -1000, 2621, 330, 330, 2757, 330, -1000, -1000, + -1000, 330, 330, 330, -1000, -1000, 327, -1000, 263, 452, + 194, 366, 3921, -1000, -1000, 3880, 3540, 3540, 3540, 3540, + -1000, 399, 395, 382, 392, 3921, -1000, 297, 194, 3200, + -1000, -1000, 3581, -1000, -1000, 468, 1315, 49, 149, -87, + -1000, -1000, 336, -1000, 336, 336, -1000, -1000, -1000, -1000, + -1000, -1000, -1000, -1000, -1000, 336, 336, 336, -1000, -1000, + -1000, -1000, -1000, -1000, -1000, 364, 364, 364, 349, 349, + 187, -1000, 420, -38, -34, -1000, -1000, -1000, -1000, 3921, + 3921, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, - -1000, -1000, -1000, -1000, -1000, 382, -1000, -1000, 3053, 19, - 2790, 2790, 283, 2790, 2790, 43, 2918, 169, 69, 2918, - 2918, 2918, 2918, 2918, 2918, 2918, 2918, 2918, 2918, 2918, - 2918, 2918, 2918, 2918, -1000, -1000, -1000, -1000, -1000, -1000, - -1000, -1000, 196, -1000, 355, -1000, -1000, -1000, -1000, 987, - 840, 2103, 229, 114, 2655, 2790, 449, 61, 114, 3905, - 2385, 2385, 2385, 2790, -1000, -1000, -1000, -1000, -1000, -1000, - 3905, 360, -1000, 3094, -1000, 342, -1000, 50, -1000, 18, - 425, 372, 348, -1000, -1000, -1000, -1000, 409, -1000, -1000, - -1000, -1000, -1000, 170, -1000, 463, 2790, 320, 1209, -1000, - -1000, -1000, -1000, -1000, -1000, -1000, -1000, 368, 432, 49, - 179, -1000, -1000, 423, -1000, 89, -87, -1000, -1000, 133, - -1000, -1000, -1000, -1000, -1000, -1000, 128, -1000, -1000, -1000, - 122, -1000, 358, -1000, 154, -1000, 3946, 3905, -1000, -1000, - 470, 2385, 3567, -1000, -1000, 3439, -1000, -1000, 1805, 43, - 104, -1000, -1000, 182, -1000, -1000, 114, 114, 1068, -1000, - -1000, -1000, -1000, 169, 2918, 2918, 2918, 245, 1068, 832, - 1086, 198, -1000, 15, 15, 8, 8, 8, 8, 8, - 649, 649, 170, -1000, 170, 2385, 319, 360, 16, -1000, - 2790, -1000, 217, 2385, 127, -1000, 2790, 170, 203, 203, - 203, -1000, 318, 303, -1000, -1000, 2520, 170, 216, 13, - 463, 3905, 2790, 2103, -1000, -1000, 2790, 367, -1000, -1000, - -1000, 455, 114, 1358, -1000, 1358, 3777, -1000, 140, -1000, - -1000, -79, 7, -1000, -1000, -1000, 297, 317, 209, 3946, - -1000, -1000, -23, 468, -1000, 309, -1000, -1000, 3, -1000, - -1000, -1000, -1000, 245, 1068, 289, -1000, 2918, 2918, -1000, - -1000, 203, 2385, 1954, 114, 721, 376, 56, -1000, 2790, - 118, -1000, -1000, -1000, -1000, 360, -1000, -1000, 359, 3777, - 3777, 455, -1000, 114, -1000, 114, 3777, -1000, 1209, -1000, - 174, -1000, 361, -1000, 42, -1000, -1000, -1000, -1000, -1000, - -1000, -1000, 119, -1000, 111, 1656, 3905, 465, 457, 1507, - -1000, 2918, 1068, 1068, -1000, 170, -1000, 170, 361, -1000, - 361, 364, 361, -51, 361, -53, 361, 361, 361, 361, - 361, 361, -1000, -102, -1000, 114, 2790, -1000, 360, -1000, - 355, 2, -1000, -1000, 162, -1000, -1000, 3777, -1000, -1000, - 428, -1000, 426, 296, 259, -1000, -1000, -1000, -1000, -1000, - 2790, 2790, -1000, 1068, -1000, -1000, -1000, -1000, -1000, -1000, - -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, 170, - 135, 114, 303, 170, 3777, 3777, -1000, -1000, -1000, -1000, - -1000, -1000, 114, 300, -1000, -1000, -1000, -1000, -1000, + -1000, -1000, -1000, -1000, -1000, 306, -1000, -1000, 3022, 24, + 2757, 2757, 251, 2757, 2757, 59, 2886, 126, 85, 2886, + 2886, 2886, 2886, 2886, 2886, 2886, 2886, 2886, 2886, 2886, + 2886, 2886, 2886, 2886, -1000, -1000, -1000, -1000, -1000, -1000, + -1000, -1000, 296, -1000, -1000, -1000, -1000, 980, 836, 2065, + 289, 176, 2621, 2757, 436, 75, 176, 3880, 2349, 2349, + 2349, 2757, -1000, -1000, -1000, -1000, -1000, -1000, 3880, 330, + -1000, 3064, -1000, 321, -1000, 67, -1000, 23, 421, 360, + 346, -1000, -1000, -1000, -1000, 390, -1000, -1000, -1000, -1000, + -1000, 194, -1000, 461, 2757, 326, 1165, -1000, -1000, -1000, + -1000, -1000, -1000, -1000, -1000, 357, 415, 87, 183, -1000, + -1000, 404, -1000, 104, -93, -1000, -1000, 147, -1000, -1000, + -1000, -1000, -1000, -1000, 146, -1000, -1000, -1000, 137, -1000, + 348, -1000, 179, -1000, 3921, 3880, -1000, -1000, 468, 2349, + 3540, -1000, -1000, 3411, -1000, -1000, 1765, 59, 79, -1000, + -1000, 254, -1000, -1000, 176, 176, 193, -1000, -1000, -1000, + -1000, 126, 2886, 2886, 2886, 15, 193, 752, 819, 763, + -1000, 208, 208, 34, 34, 34, 34, 34, 86, 86, + -1000, 194, 2349, 325, 330, 18, -1000, 2757, -1000, 138, + 2349, 76, -1000, 2757, 194, 203, 203, 203, -1000, 303, + 243, -1000, -1000, 2485, 194, 258, 17, 461, 3880, 2757, + 2065, -1000, -1000, 2757, 353, -1000, -1000, -1000, 452, 176, + 1315, -1000, 1315, 3751, -1000, 163, -1000, -1000, -83, 10, + -1000, -1000, -1000, 223, 283, 249, 3921, -1000, -1000, -25, + 466, -1000, 271, -1000, -1000, 12, -1000, -1000, -1000, -1000, + 15, 193, 679, -1000, 2886, 2886, -1000, 203, 2349, 1915, + 176, 198, 313, 69, -1000, 2757, 276, -1000, -1000, -1000, + -1000, 330, -1000, -1000, 362, 3751, 3751, 452, -1000, 176, + -1000, 176, 3751, -1000, 1165, -1000, 197, -1000, 336, -1000, + 40, -1000, -1000, -1000, -1000, -1000, -1000, -1000, 136, -1000, + 134, 1615, 3880, 463, 454, 1465, -1000, 2886, 193, 193, + -1000, 194, -1000, 194, 336, -1000, 336, 349, 336, -54, + 336, -55, 336, 336, 336, 336, 336, 336, -1000, -107, + -1000, 176, 2757, -1000, 330, -1000, 296, -1, -1000, -1000, + 174, -1000, -1000, 3751, -1000, -1000, 411, -1000, 408, 216, + 209, -1000, -1000, -1000, -1000, -1000, 2757, 2757, -1000, 193, + -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, + -1000, -1000, -1000, -1000, -1000, 194, 160, 176, 243, 194, + 3751, 3751, -1000, -1000, -1000, -1000, -1000, -1000, 176, 226, + -1000, -1000, -1000, -1000, -1000, } var yyPgo = [...]int{ - 0, 646, 17, 411, 645, 642, 641, 639, 635, 634, - 633, 632, 629, 627, 623, 448, 618, 615, 614, 27, - 609, 16, 92, 8, 29, 3, 607, 21, 69, 41, - 606, 31, 604, 593, 47, 592, 32, 591, 590, 25, - 589, 587, 586, 585, 579, 442, 577, 572, 571, 563, - 552, 13, 4, 2, 6, 7, 551, 65, 46, 549, - 12, 548, 547, 545, 544, 28, 543, 38, 542, 26, - 541, 539, 34, 20, 536, 535, 114, 63, 534, 36, - 0, 11, 37, 15, 533, 620, 14, 42, 532, 531, - 529, 528, 527, 526, 525, 523, 522, 520, 126, 519, - 518, 517, 40, 9, 513, 512, 511, 33, 510, 22, - 509, 508, 506, 10, 5, 505, 1, 504, 35, 503, - 502, 497, 48, 45, 495, 492, + 0, 635, 17, 414, 631, 630, 629, 628, 623, 621, + 619, 614, 612, 611, 605, 447, 604, 602, 600, 33, + 598, 15, 53, 8, 29, 3, 589, 22, 66, 41, + 588, 35, 587, 586, 51, 585, 58, 584, 583, 31, + 582, 577, 569, 561, 560, 439, 556, 549, 546, 545, + 544, 13, 4, 2, 6, 7, 542, 262, 16, 541, + 12, 540, 539, 538, 536, 10, 535, 32, 533, 26, + 532, 531, 38, 20, 530, 133, 60, 529, 27, 0, + 11, 37, 21, 528, 616, 14, 42, 526, 525, 524, + 522, 520, 518, 517, 516, 515, 513, 126, 512, 511, + 510, 40, 9, 509, 508, 507, 34, 506, 28, 503, + 502, 501, 25, 5, 496, 1, 494, 36, 492, 490, + 489, 48, 45, 487, 486, } var yyR1 = [...]int{ - 0, 120, 121, 121, 1, 1, 1, 1, 1, 1, + 0, 119, 120, 120, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 18, 18, 18, 7, 8, 9, 9, - 12, 110, 111, 111, 111, 107, 92, 92, 92, 95, - 95, 93, 93, 93, 93, 93, 93, 94, 94, 94, - 94, 94, 96, 96, 96, 96, 96, 97, 97, 97, - 97, 97, 97, 97, 97, 97, 97, 98, 98, 102, - 102, 103, 103, 103, 100, 100, 101, 101, 104, 104, - 104, 99, 99, 99, 99, 99, 99, 105, 105, 106, - 106, 106, 106, 106, 108, 115, 115, 115, 115, 109, - 109, 117, 117, 116, 112, 112, 112, 113, 113, 113, - 114, 114, 114, 10, 10, 10, 118, 118, 11, 11, - 13, 13, 13, 13, 13, 119, 119, 14, 14, 14, - 125, 15, 16, 16, 17, 17, 19, 19, 23, 23, - 22, 22, 24, 24, 24, 24, 84, 84, 84, 83, - 83, 26, 27, 27, 28, 28, 29, 29, 29, 29, + 12, 109, 110, 110, 110, 106, 91, 91, 91, 94, + 94, 92, 92, 92, 92, 92, 92, 93, 93, 93, + 93, 93, 95, 95, 95, 95, 95, 96, 96, 96, + 96, 96, 96, 96, 96, 96, 96, 97, 97, 101, + 101, 102, 102, 102, 99, 99, 100, 100, 103, 103, + 103, 98, 98, 98, 98, 98, 98, 104, 104, 105, + 105, 105, 105, 105, 107, 114, 114, 114, 114, 108, + 108, 116, 116, 115, 111, 111, 111, 112, 112, 112, + 113, 113, 113, 10, 10, 10, 117, 117, 11, 11, + 13, 13, 13, 13, 13, 118, 118, 14, 14, 14, + 124, 15, 16, 16, 17, 17, 19, 19, 23, 23, + 22, 22, 24, 24, 24, 24, 83, 83, 83, 82, + 82, 26, 27, 27, 28, 28, 29, 29, 29, 29, 38, 71, 71, 30, 30, 30, 31, 31, 32, 32, - 89, 89, 88, 88, 88, 87, 87, 33, 33, 33, + 88, 88, 87, 87, 87, 86, 86, 33, 33, 33, 34, 34, 35, 35, 37, 37, 36, 36, 39, 39, - 25, 25, 25, 25, 25, 25, 25, 75, 75, 41, - 41, 40, 40, 40, 40, 40, 40, 40, 40, 40, - 40, 50, 50, 50, 50, 50, 50, 42, 42, 42, - 42, 42, 42, 42, 21, 21, 51, 51, 51, 57, - 52, 52, 45, 45, 45, 45, 45, 45, 45, 45, + 25, 25, 25, 25, 25, 25, 41, 41, 40, 40, + 40, 40, 40, 40, 40, 40, 40, 40, 50, 50, + 50, 50, 50, 50, 42, 42, 42, 42, 42, 42, + 42, 21, 21, 51, 51, 51, 57, 52, 52, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, - 45, 45, 45, 45, 45, 45, 45, 45, 48, 48, - 48, 46, 46, 46, 46, 47, 47, 47, 49, 49, - 49, 91, 91, 91, 91, 91, 91, 91, 91, 91, - 91, 91, 91, 91, 91, 91, 91, 91, 61, 61, - 20, 20, 59, 59, 60, 62, 62, 58, 58, 58, - 44, 44, 44, 44, 44, 44, 44, 63, 63, 64, - 64, 65, 65, 66, 66, 67, 68, 68, 68, 69, - 69, 69, 69, 43, 43, 43, 43, 43, 43, 70, - 70, 70, 70, 53, 53, 55, 55, 54, 56, 72, - 72, 73, 76, 76, 77, 77, 74, 74, 78, 78, - 78, 81, 81, 82, 82, 85, 85, 86, 86, 79, - 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, - 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, - 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, - 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, + 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, + 45, 45, 45, 45, 45, 48, 48, 48, 46, 46, + 46, 46, 47, 47, 47, 49, 49, 49, 90, 90, + 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, + 90, 90, 90, 90, 90, 61, 61, 20, 20, 59, + 59, 60, 62, 62, 58, 58, 58, 44, 44, 44, + 44, 44, 44, 44, 44, 63, 63, 64, 64, 65, + 65, 66, 66, 67, 68, 68, 68, 69, 69, 69, + 69, 43, 43, 43, 43, 43, 43, 70, 70, 70, + 70, 53, 53, 55, 55, 54, 56, 72, 72, 73, + 75, 75, 76, 76, 74, 74, 77, 77, 77, 80, + 80, 81, 81, 84, 84, 85, 85, 78, 78, 78, + 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, + 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, + 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, + 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, + 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, + 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, + 78, 78, 78, 78, 78, 78, 78, 78, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, - 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, - 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, - 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, - 80, 80, 80, 80, 80, 80, 122, 123, 124, 90, - 90, 90, + 79, 79, 79, 79, 121, 122, 123, 89, 89, 89, } var yyR2 = [...]int{ @@ -1019,22 +1018,22 @@ var yyR2 = [...]int{ 2, 1, 3, 4, 4, 3, 2, 4, 0, 1, 0, 1, 0, 1, 2, 1, 1, 1, 2, 2, 2, 3, 2, 2, 2, 1, 1, 3, 0, 2, - 1, 3, 3, 2, 3, 1, 2, 0, 3, 1, - 1, 3, 3, 4, 4, 5, 3, 4, 5, 6, - 2, 1, 2, 1, 2, 1, 2, 1, 1, 1, - 1, 1, 1, 1, 0, 2, 1, 1, 1, 3, - 1, 3, 1, 1, 1, 1, 1, 3, 3, 3, - 3, 3, 3, 3, 3, 3, 3, 3, 3, 2, - 2, 2, 2, 3, 1, 1, 1, 1, 4, 5, - 6, 6, 7, 5, 4, 1, 1, 1, 4, 4, - 4, 2, 1, 2, 2, 2, 1, 2, 2, 1, - 2, 2, 2, 2, 2, 2, 2, 1, 0, 1, - 0, 2, 1, 2, 4, 0, 2, 1, 3, 5, - 1, 1, 1, 1, 1, 1, 1, 0, 3, 0, - 2, 0, 3, 1, 3, 2, 0, 1, 1, 0, - 2, 4, 4, 2, 1, 3, 5, 4, 6, 1, - 3, 3, 5, 1, 3, 1, 2, 3, 1, 1, - 3, 3, 0, 2, 0, 3, 0, 1, 0, 1, + 1, 3, 3, 2, 3, 1, 1, 1, 3, 3, + 4, 4, 5, 3, 4, 5, 6, 2, 1, 2, + 1, 2, 1, 2, 1, 1, 1, 1, 1, 1, + 1, 0, 2, 1, 1, 1, 3, 1, 3, 1, + 1, 1, 1, 1, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 2, 2, 2, 2, + 3, 1, 1, 1, 1, 4, 5, 6, 6, 7, + 5, 4, 1, 1, 1, 4, 4, 4, 2, 1, + 2, 2, 2, 1, 2, 2, 1, 2, 2, 2, + 2, 2, 2, 2, 1, 0, 1, 0, 2, 1, + 2, 4, 0, 2, 1, 3, 5, 1, 1, 1, + 1, 1, 1, 1, 1, 0, 3, 0, 2, 0, + 3, 1, 3, 2, 0, 1, 1, 0, 2, 4, + 4, 2, 1, 3, 5, 4, 6, 1, 3, 3, + 5, 1, 3, 1, 2, 3, 1, 1, 3, 3, + 0, 2, 0, 3, 0, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, @@ -1046,176 +1045,175 @@ var yyR2 = [...]int{ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, - 1, 1, + 1, 1, 1, 1, 1, 1, 0, 0, 1, 1, } var yyChk = [...]int{ - -1000, -120, -1, -2, -6, -7, -8, -9, -10, -11, - -13, -14, -3, -4, -18, 8, 9, -12, 93, 94, - 95, 108, 23, 109, 112, 6, -122, 7, 149, 39, - -121, 154, -65, 14, -17, 5, -15, -125, -15, -15, - -110, 39, -78, 98, 103, -81, 42, -80, 118, 137, - 131, 129, 110, 127, 123, 121, 122, 107, 116, 117, - 29, 140, 136, 139, 115, 135, 132, 124, 17, 104, - 120, 142, 114, 133, 125, 126, 138, 113, 134, 143, - 153, 130, 128, 144, 98, 98, 99, 93, 99, 98, - -119, 54, -36, -85, 42, -80, -36, -124, -15, -2, - 56, -69, 16, 15, -5, -3, -122, 18, -74, 101, - -16, -27, -28, -29, -30, -38, -57, -122, -36, 10, - -111, -107, 42, 99, -77, 102, -36, -76, 102, -76, - 98, 10, -36, 145, 55, 92, -19, 19, -123, 41, - 149, -25, -40, 58, -45, 26, 21, -44, -41, -58, - -56, -57, 81, 82, 89, 59, 91, -48, -46, -47, - -49, 44, 43, 45, 46, 47, 48, 51, 52, 53, - -81, -85, -54, -122, 150, 151, 61, 28, 146, 147, - 148, 102, 87, 149, 42, -80, -66, -67, -25, -65, - -2, -37, 24, -36, 50, 27, 40, -33, -34, -35, - 30, 33, 35, 31, 36, -89, 20, -27, -2, -122, - -88, -87, 20, -85, 44, -36, 41, 40, -92, -95, - -97, -96, -93, -94, 129, 130, 133, 134, 135, 136, - 137, 138, 139, 140, 110, 125, 126, 127, 128, 113, - 114, 115, 116, 117, 118, 120, 121, 122, 123, 124, - -77, -36, 58, 97, 96, -36, 21, -36, -36, 98, - 10, -86, -85, -79, 97, 57, 20, 22, 141, 60, - 15, 61, 93, 35, 147, 148, 146, 26, 9, 23, - 109, 19, 85, 95, 64, 65, 111, 21, 53, 10, - 12, 13, 102, 101, 75, 99, 33, 7, 91, 24, - 72, 30, 25, 31, 73, 16, 87, 36, 58, 51, - 37, 56, 14, 34, 74, 96, 149, 32, 6, 152, - 27, 108, 98, 145, 63, 100, 52, 5, 103, 8, - 38, 28, 62, 11, 112, -22, -24, 83, -25, -85, - 57, 56, 72, 40, 17, -25, -42, 75, 58, 73, - 74, 60, 78, 77, 88, 81, 82, 83, 84, 85, - 86, 87, 79, 80, 66, 67, 68, 69, 70, 71, - 76, -75, -122, -57, -122, -45, -45, -45, -45, -45, - -122, 92, -52, -25, -122, -122, -122, -61, -25, -122, - -122, -122, -122, 40, -68, 22, 23, -69, -123, -43, - 27, 28, -2, -122, -36, -72, -73, -58, -81, -85, - -28, -29, -28, -29, 30, 30, 30, 34, 30, -34, - -85, -123, -123, -2, -87, -39, 11, -112, -113, -114, - -82, 44, 45, -81, -79, -107, -108, -115, 104, 103, - -109, 99, 25, -104, 51, 58, -100, 143, -98, 39, - -98, -98, -98, -98, -98, -102, 39, -102, -102, -103, - 39, -103, 42, 21, -118, 105, 100, -118, -36, -36, - -26, 40, 10, -84, -83, 20, -81, 44, 92, -25, - -25, -50, 51, 58, 52, 53, -25, -25, -45, -51, - -54, -57, 49, 75, 73, 74, 60, -45, -45, -45, + -1000, -119, -1, -2, -6, -7, -8, -9, -10, -11, + -13, -14, -3, -4, -18, 8, 9, -12, 94, 95, + 96, 109, 23, 110, 113, 6, -121, 7, 150, 39, + -120, 155, -65, 14, -17, 5, -15, -124, -15, -15, + -109, 39, -77, 99, 104, -80, 42, -79, 119, 138, + 132, 130, 111, 128, 124, 122, 123, 108, 117, 118, + 29, 141, 137, 140, 116, 136, 133, 125, 17, 105, + 121, 143, 115, 134, 126, 127, 139, 114, 135, 144, + 154, 131, 129, 145, 99, 99, 100, 94, 100, 99, + -118, 55, -36, -84, 42, -79, -36, -123, -15, -2, + 57, -69, 16, 15, -5, -3, -121, 18, -74, 102, + -16, -27, -28, -29, -30, -38, -57, -121, -36, 10, + -110, -106, 42, 100, -76, 103, -36, -75, 103, -75, + 99, 10, -36, 146, 56, 93, -19, 19, -122, 41, + 150, -25, -40, 59, -45, 21, -44, -41, -58, -56, + -57, 82, 83, 90, 60, 92, -48, -46, -47, -49, + 44, 43, 45, 46, 47, 48, 49, 52, 53, 54, + -80, -84, -54, -121, 151, 152, 62, 28, 147, 148, + 149, 103, 88, 150, 42, -79, -66, -67, -25, -65, + -2, -37, 24, -36, 51, 27, 40, -33, -34, -35, + 30, 33, 35, 31, 36, -88, 20, -27, -2, -121, + -87, -86, 20, -84, 44, -36, 41, 40, -91, -94, + -96, -95, -92, -93, 130, 131, 134, 135, 136, 137, + 138, 139, 140, 141, 111, 126, 127, 128, 129, 114, + 115, 116, 117, 118, 119, 121, 122, 123, 124, 125, + -76, -36, 59, 98, 97, -36, 21, -36, -36, 99, + 10, -85, -84, -78, 98, 58, 20, 22, 142, 61, + 15, 62, 94, 35, 148, 149, 147, 26, 9, 23, + 110, 19, 86, 96, 65, 66, 112, 21, 54, 10, + 12, 13, 103, 102, 76, 100, 33, 7, 92, 24, + 73, 30, 25, 31, 74, 16, 88, 36, 59, 52, + 37, 57, 14, 34, 75, 97, 150, 32, 6, 153, + 27, 109, 99, 146, 64, 101, 53, 5, 104, 8, + 38, 28, 63, 11, 113, -22, -24, 84, -25, -84, + 58, 57, 73, 40, 17, -25, -42, 76, 59, 74, + 75, 61, 79, 78, 89, 82, 83, 84, 85, 86, + 87, 88, 80, 81, 67, 68, 69, 70, 71, 72, + 77, -57, -121, -45, -45, -45, -45, -45, -121, 93, + -52, -25, -121, -121, -121, -61, -25, -121, -121, -121, + -121, 40, -68, 22, 23, -69, -122, -43, 27, 28, + -2, -121, -36, -72, -73, -58, -80, -84, -28, -29, + -28, -29, 30, 30, 30, 34, 30, -34, -84, -122, + -122, -2, -86, -39, 11, -111, -112, -113, -81, 44, + 45, -80, -78, -106, -107, -114, 105, 104, -108, 100, + 25, -103, 52, 59, -99, 144, -97, 39, -97, -97, + -97, -97, -97, -101, 39, -101, -101, -102, 39, -102, + 42, 21, -117, 106, 101, -117, -36, -36, -26, 40, + 10, -83, -82, 20, -80, 44, 93, -25, -25, -50, + 52, 59, 53, 54, -25, -25, -45, -51, -54, -57, + 50, 76, 74, 75, 61, -45, -45, -45, -45, -45, -45, -45, -45, -45, -45, -45, -45, -45, -45, -45, - -45, -45, 42, -81, -23, 19, -22, -82, -86, -79, - 40, -123, -25, -19, -59, -60, 62, -58, -22, -22, - -22, -67, -72, -53, -55, -54, -122, -2, -70, -81, - -39, 40, 66, 92, -32, -31, 37, 38, -31, 30, - -123, -65, -25, 40, -114, 66, 39, 25, -109, 42, - 42, -99, 26, 51, -101, 144, 45, 45, 45, 37, - -107, -36, -58, -39, -24, -27, -83, 83, -86, 51, - 52, 53, -51, -45, -45, -45, -21, 111, 57, -123, - -123, -22, -122, 92, -25, 20, -22, -62, -60, 64, - -25, -123, -123, -123, -123, 40, -123, -123, -123, 40, - 92, -65, -73, -25, -82, -25, 39, -69, -113, -114, - -117, -116, -81, 42, -105, 141, 44, 45, 46, 51, - 146, 41, 40, 41, 40, -36, 100, -63, 12, 92, - -21, 57, -45, -45, -123, -23, -82, -91, 129, 110, - 127, 123, 132, 142, 125, 143, 126, 116, 113, 114, - 115, 118, 44, -65, 65, -25, 63, -55, 28, -2, - -122, -81, -81, -69, -71, -81, 41, 40, -98, -106, - 104, 25, 103, 45, 45, -90, -122, -82, -58, -64, - 13, 15, 83, -45, -123, -123, -98, -98, -103, -98, - 117, -98, 117, -98, -98, -98, -98, -98, -98, -20, - 152, -25, -53, -2, 92, 40, 41, -116, 25, 25, - 41, 41, -25, -52, -123, 44, -123, -81, -81, + -80, -23, 19, -22, -81, -85, -78, 40, -122, -25, + -19, -59, -60, 63, -58, -22, -22, -22, -67, -72, + -53, -55, -54, -121, -2, -70, -80, -39, 40, 67, + 93, -32, -31, 37, 38, -31, 30, -122, -65, -25, + 40, -113, 67, 39, 25, -108, 42, 42, -98, 26, + 52, -100, 145, 45, 45, 45, 37, -106, -36, -58, + -39, -24, -27, -82, 84, -85, 52, 53, 54, -51, + -45, -45, -45, -21, 112, 58, -122, -22, -121, 93, + -25, 20, -22, -62, -60, 65, -25, -122, -122, -122, + -122, 40, -122, -122, -122, 40, 93, -65, -73, -25, + -81, -25, 39, -69, -112, -113, -116, -115, -80, 42, + -104, 142, 44, 45, 46, 52, 147, 41, 40, 41, + 40, -36, 101, -63, 12, 93, -21, 58, -45, -45, + -122, -23, -81, -90, 130, 111, 128, 124, 133, 143, + 126, 144, 127, 117, 114, 115, 116, 119, 44, -65, + 66, -25, 64, -55, 28, -2, -121, -80, -80, -69, + -71, -80, 41, 40, -97, -105, 105, 25, 104, 45, + 45, -89, -121, -81, -58, -64, 13, 15, 84, -45, + -122, -122, -97, -97, -102, -97, 118, -97, 118, -97, + -97, -97, -97, -97, -97, -20, 153, -25, -53, -2, + 93, 40, 41, -115, 25, 25, 41, 41, -25, -52, + -122, 44, -122, -80, -80, } var yyDef = [...]int{ 0, -2, 2, -2, 5, 6, 7, 8, 9, 10, - 11, 12, 311, 0, 130, 130, 130, 0, 348, 0, - 0, 125, 0, 0, 468, 130, 0, 23, 24, 466, - 1, 3, 319, 0, 0, 134, 346, 132, 0, 0, - 28, 0, 0, 344, 349, 350, 351, 352, 430, 431, - 432, 433, 434, 435, 436, 437, 438, 439, 440, 441, - 442, 443, 444, 445, 446, 447, 448, 449, 450, 451, - 452, 453, 454, 455, 456, 457, 458, 459, 460, 461, - 462, 463, 464, 465, 0, 342, 342, 0, 0, 0, - 0, 126, 127, 186, 355, 356, 128, 129, 136, 17, - 0, 13, 0, 0, 311, 19, 0, 135, 0, 347, + 11, 12, 309, 0, 130, 130, 130, 0, 346, 0, + 0, 125, 0, 0, 466, 130, 0, 23, 24, 464, + 1, 3, 317, 0, 0, 134, 344, 132, 0, 0, + 28, 0, 0, 342, 347, 348, 349, 350, 428, 429, + 430, 431, 432, 433, 434, 435, 436, 437, 438, 439, + 440, 441, 442, 443, 444, 445, 446, 447, 448, 449, + 450, 451, 452, 453, 454, 455, 456, 457, 458, 459, + 460, 461, 462, 463, 0, 340, 340, 0, 0, 0, + 0, 126, 127, 186, 353, 354, 128, 129, 136, 17, + 0, 13, 0, 0, 309, 19, 0, 135, 0, 345, 131, 0, 152, 154, 155, 156, -2, 0, 172, 0, - 0, 32, 0, 344, 0, 0, 0, 0, 0, 0, - 0, 0, 122, 123, 0, 0, 0, 137, 18, 467, - 25, 320, 190, 0, 195, 197, 0, 232, 233, 234, - 235, 236, 0, 0, 0, 0, 0, 254, 255, 256, - 257, 300, 301, 302, 303, 304, 305, 306, 199, 200, - 297, 0, 338, 0, 0, 0, 288, 0, 265, 266, - 267, 0, 0, 0, -2, -2, 312, 313, 316, 319, + 0, 32, 0, 342, 0, 0, 0, 0, 0, 0, + 0, 0, 122, 123, 0, 0, 0, 137, 18, 465, + 25, 318, 190, 0, 195, 0, 229, 230, 231, 232, + 233, 0, 0, 0, 0, 0, 251, 252, 253, 254, + 297, 298, 299, 300, 301, 302, 303, 304, 196, 197, + 294, 0, 336, 0, 0, 0, 285, 0, 262, 263, + 264, 0, 0, 0, -2, -2, 310, 311, 314, 317, 17, 0, 0, 185, 133, 0, 0, 0, 0, 0, 177, 0, 0, 0, 0, 0, 171, 0, 17, 0, 160, 173, 0, 175, 176, 188, 104, 0, 78, 74, 37, 38, 67, 40, 67, 67, 59, 60, 61, 62, 63, 64, 65, 66, 52, 67, 67, 67, 56, 41, 42, 43, 44, 45, 46, 69, 69, 69, 71, 71, - 0, 30, 0, 116, 116, 118, 343, 119, 120, 0, - 0, 187, 357, 358, 359, 360, 361, 362, 363, 364, - 365, 366, 367, 368, 369, 370, 371, 372, 373, 374, - 375, 376, 377, 378, 379, 380, 381, 382, 383, 384, - 385, 386, 387, 388, 389, 390, 391, 392, 393, 394, - 395, 396, 397, 398, 399, 400, 401, 402, 403, 404, - 405, 406, 407, 408, 409, 410, 411, 412, 413, 414, - 415, 416, 417, 418, 419, 420, 421, 422, 423, 424, - 425, 426, 427, 428, 429, 16, 140, 142, 146, 0, + 0, 30, 0, 116, 116, 118, 341, 119, 120, 0, + 0, 187, 355, 356, 357, 358, 359, 360, 361, 362, + 363, 364, 365, 366, 367, 368, 369, 370, 371, 372, + 373, 374, 375, 376, 377, 378, 379, 380, 381, 382, + 383, 384, 385, 386, 387, 388, 389, 390, 391, 392, + 393, 394, 395, 396, 397, 398, 399, 400, 401, 402, + 403, 404, 405, 406, 407, 408, 409, 410, 411, 412, + 413, 414, 415, 416, 417, 418, 419, 420, 421, 422, + 423, 424, 425, 426, 427, 16, 140, 142, 146, 0, 0, 0, 0, 0, 0, 193, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 217, 218, 219, 220, 221, 222, - 223, 196, 0, 210, 0, 249, 250, 251, 252, 0, - 138, 0, 0, 230, 0, 0, 136, 0, 289, 0, - 0, 0, 0, 0, 315, 317, 318, 14, 20, 21, - 0, 0, -2, 0, 184, 188, 339, 0, 297, 0, - 153, 168, 0, 165, 178, 179, 180, 0, 182, 183, - 158, 159, 229, 17, 174, 311, 0, 31, 105, 107, - 110, 111, 112, 353, 354, 33, 34, 0, 0, 0, - 0, 99, 100, 81, 79, 0, 76, 75, 39, 0, - 57, 58, 53, 54, 55, 47, 0, 48, 49, 50, - 0, 51, 0, 345, 0, 117, 0, 0, 121, 124, - 188, 0, 0, 143, 147, 0, 149, 150, 0, 191, - 192, 194, 211, 0, 213, 215, 321, 322, 201, 202, - 226, 227, 228, 0, 0, 0, 0, 224, 206, 0, - 237, 238, 239, 240, 241, 242, 243, 244, 245, 246, - 247, 248, 0, 253, 0, 0, 139, 298, 0, -2, - 0, 337, 0, 0, 295, 292, 0, 0, 0, 0, - 0, 314, 22, 323, 333, 335, 0, 17, 0, 329, - 311, 0, 0, 0, 163, 169, 0, 0, 164, 181, - -2, 319, 189, 0, 108, 0, 0, 95, 0, 97, - 98, 87, 0, 80, 36, 77, 0, 0, 0, 0, - 113, 114, 0, 307, 141, 151, 148, 144, 0, 212, - 214, 216, 203, 224, 207, 0, 204, 0, 0, 198, - 258, 0, 138, 0, 231, 0, 311, 0, 293, 0, - 0, 264, 268, 269, 270, 0, 336, -2, 0, 0, - 0, 319, 340, 341, 298, 166, 0, 27, 106, 109, - 0, 101, 67, 96, 89, 88, 82, 83, 84, 85, - 86, 68, 0, 72, 0, 469, 0, 309, 0, 0, - 205, 0, 225, 208, 259, 0, 299, 0, 67, 272, - 67, 71, 67, 276, 67, 279, 67, 67, 67, 67, - 67, 67, 287, 290, 263, 296, 0, 334, 0, -2, - 0, 331, 330, 26, 0, 161, 94, 0, 103, 35, - 0, 91, 93, 0, 0, 29, 470, 471, 115, 15, - 0, 0, 145, 209, 260, 261, 271, 273, 274, 275, - 277, 278, 280, 281, 282, 283, 284, 285, 286, 0, - 0, 294, 326, 17, 0, 0, 167, 102, 90, 92, - 70, 73, 310, 308, 262, 291, -2, 332, 162, + 0, 0, 0, 0, 214, 215, 216, 217, 218, 219, + 220, 207, 0, 246, 247, 248, 249, 0, 138, 0, + 0, 227, 0, 0, 136, 0, 286, 0, 0, 0, + 0, 0, 313, 315, 316, 14, 20, 21, 0, 0, + -2, 0, 184, 188, 337, 0, 294, 0, 153, 168, + 0, 165, 178, 179, 180, 0, 182, 183, 158, 159, + 226, 17, 174, 309, 0, 31, 105, 107, 110, 111, + 112, 351, 352, 33, 34, 0, 0, 0, 0, 99, + 100, 81, 79, 0, 76, 75, 39, 0, 57, 58, + 53, 54, 55, 47, 0, 48, 49, 50, 0, 51, + 0, 343, 0, 117, 0, 0, 121, 124, 188, 0, + 0, 143, 147, 0, 149, 150, 0, 191, 192, 194, + 208, 0, 210, 212, 319, 320, 198, 199, 223, 224, + 225, 0, 0, 0, 0, 221, 203, 0, 234, 235, + 236, 237, 238, 239, 240, 241, 242, 243, 244, 245, + 250, 0, 0, 139, 295, 0, -2, 0, 335, 0, + 0, 292, 289, 0, 0, 0, 0, 0, 312, 22, + 321, 331, 333, 0, 17, 0, 327, 309, 0, 0, + 0, 163, 169, 0, 0, 164, 181, -2, 317, 189, + 0, 108, 0, 0, 95, 0, 97, 98, 87, 0, + 80, 36, 77, 0, 0, 0, 0, 113, 114, 0, + 305, 141, 151, 148, 144, 0, 209, 211, 213, 200, + 221, 204, 0, 201, 0, 0, 255, 0, 138, 0, + 228, 0, 309, 0, 290, 0, 0, 261, 265, 266, + 267, 0, 334, -2, 0, 0, 0, 317, 338, 339, + 295, 166, 0, 27, 106, 109, 0, 101, 67, 96, + 89, 88, 82, 83, 84, 85, 86, 68, 0, 72, + 0, 467, 0, 307, 0, 0, 202, 0, 222, 205, + 256, 0, 296, 0, 67, 269, 67, 71, 67, 273, + 67, 276, 67, 67, 67, 67, 67, 67, 284, 287, + 260, 293, 0, 332, 0, -2, 0, 329, 328, 26, + 0, 161, 94, 0, 103, 35, 0, 91, 93, 0, + 0, 29, 468, 469, 115, 15, 0, 0, 145, 206, + 257, 258, 268, 270, 271, 272, 274, 275, 277, 278, + 279, 280, 281, 282, 283, 0, 0, 291, 324, 17, + 0, 0, 167, 102, 90, 92, 70, 73, 308, 306, + 259, 288, -2, 330, 162, } var yyTok1 = [...]int{ 1, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, - 3, 3, 3, 59, 3, 3, 3, 86, 78, 3, - 39, 41, 83, 81, 40, 82, 92, 84, 3, 3, - 3, 3, 3, 3, 3, 3, 3, 3, 3, 154, - 67, 66, 68, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 60, 3, 3, 3, 87, 79, 3, + 39, 41, 84, 82, 40, 83, 93, 85, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 155, + 68, 67, 69, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, - 3, 3, 3, 3, 88, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 89, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, - 3, 3, 3, 3, 77, 3, 89, + 3, 3, 3, 3, 78, 3, 90, } var yyTok2 = [...]int{ @@ -1224,15 +1222,15 @@ var yyTok2 = [...]int{ 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, - 55, 56, 57, 58, 60, 61, 62, 63, 64, 65, - 69, 70, 71, 72, 73, 74, 75, 76, 79, 80, - 85, 87, 90, 91, 93, 94, 95, 96, 97, 98, + 55, 56, 57, 58, 59, 61, 62, 63, 64, 65, + 66, 70, 71, 72, 73, 74, 75, 76, 77, 80, + 81, 86, 88, 91, 92, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, 127, 128, 129, 130, 131, 132, 133, 134, 135, 136, 137, 138, 139, 140, 141, 142, 143, 144, 145, 146, 147, 148, - 149, 150, 151, 152, 153, + 149, 150, 151, 152, 153, 154, } var yyTok3 = [...]int{ 0, @@ -2709,326 +2707,308 @@ yydefault: yyVAL.expr = yyDollar[1].expr } case 196: - yyDollar = yyS[yypt-2 : yypt+1] - //line sql.y:1141 - { - yyVAL.expr = &Default{ColName: yyDollar[2].str} - } - case 197: - yyDollar = yyS[yypt-0 : yypt+1] - //line sql.y:1147 - { - yyVAL.str = "" - } - case 198: - yyDollar = yyS[yypt-3 : yypt+1] - //line sql.y:1151 - { - yyVAL.str = string(yyDollar[2].bytes) - } - case 199: yyDollar = yyS[yypt-1 : yypt+1] - //line sql.y:1157 + //line sql.y:1143 { yyVAL.boolVal = BoolVal(true) } - case 200: + case 197: yyDollar = yyS[yypt-1 : yypt+1] - //line sql.y:1161 + //line sql.y:1147 { yyVAL.boolVal = BoolVal(false) } - case 201: + case 198: yyDollar = yyS[yypt-3 : yypt+1] - //line sql.y:1167 + //line sql.y:1153 { yyVAL.expr = &ComparisonExpr{Left: yyDollar[1].expr, Operator: yyDollar[2].str, Right: yyDollar[3].expr} } - case 202: + case 199: yyDollar = yyS[yypt-3 : yypt+1] - //line sql.y:1171 + //line sql.y:1157 { yyVAL.expr = &ComparisonExpr{Left: yyDollar[1].expr, Operator: InStr, Right: yyDollar[3].colTuple} } - case 203: + case 200: yyDollar = yyS[yypt-4 : yypt+1] - //line sql.y:1175 + //line sql.y:1161 { yyVAL.expr = &ComparisonExpr{Left: yyDollar[1].expr, Operator: NotInStr, Right: yyDollar[4].colTuple} } - case 204: + case 201: yyDollar = yyS[yypt-4 : yypt+1] - //line sql.y:1179 + //line sql.y:1165 { yyVAL.expr = &ComparisonExpr{Left: yyDollar[1].expr, Operator: LikeStr, Right: yyDollar[3].expr, Escape: yyDollar[4].expr} } - case 205: + case 202: yyDollar = yyS[yypt-5 : yypt+1] - //line sql.y:1183 + //line sql.y:1169 { yyVAL.expr = &ComparisonExpr{Left: yyDollar[1].expr, Operator: NotLikeStr, Right: yyDollar[4].expr, Escape: yyDollar[5].expr} } - case 206: + case 203: yyDollar = yyS[yypt-3 : yypt+1] - //line sql.y:1187 + //line sql.y:1173 { yyVAL.expr = &ComparisonExpr{Left: yyDollar[1].expr, Operator: RegexpStr, Right: yyDollar[3].expr} } - case 207: + case 204: yyDollar = yyS[yypt-4 : yypt+1] - //line sql.y:1191 + //line sql.y:1177 { yyVAL.expr = &ComparisonExpr{Left: yyDollar[1].expr, Operator: NotRegexpStr, Right: yyDollar[4].expr} } - case 208: + case 205: yyDollar = yyS[yypt-5 : yypt+1] - //line sql.y:1195 + //line sql.y:1181 { yyVAL.expr = &RangeCond{Left: yyDollar[1].expr, Operator: BetweenStr, From: yyDollar[3].expr, To: yyDollar[5].expr} } - case 209: + case 206: yyDollar = yyS[yypt-6 : yypt+1] - //line sql.y:1199 + //line sql.y:1185 { yyVAL.expr = &RangeCond{Left: yyDollar[1].expr, Operator: NotBetweenStr, From: yyDollar[4].expr, To: yyDollar[6].expr} } - case 210: + case 207: yyDollar = yyS[yypt-2 : yypt+1] - //line sql.y:1203 + //line sql.y:1189 { yyVAL.expr = &ExistsExpr{Subquery: yyDollar[2].subquery} } - case 211: + case 208: yyDollar = yyS[yypt-1 : yypt+1] - //line sql.y:1209 + //line sql.y:1195 { yyVAL.str = IsNullStr } - case 212: + case 209: yyDollar = yyS[yypt-2 : yypt+1] - //line sql.y:1213 + //line sql.y:1199 { yyVAL.str = IsNotNullStr } - case 213: + case 210: yyDollar = yyS[yypt-1 : yypt+1] - //line sql.y:1217 + //line sql.y:1203 { yyVAL.str = IsTrueStr } - case 214: + case 211: yyDollar = yyS[yypt-2 : yypt+1] - //line sql.y:1221 + //line sql.y:1207 { yyVAL.str = IsNotTrueStr } - case 215: + case 212: yyDollar = yyS[yypt-1 : yypt+1] - //line sql.y:1225 + //line sql.y:1211 { yyVAL.str = IsFalseStr } - case 216: + case 213: yyDollar = yyS[yypt-2 : yypt+1] - //line sql.y:1229 + //line sql.y:1215 { yyVAL.str = IsNotFalseStr } - case 217: + case 214: yyDollar = yyS[yypt-1 : yypt+1] - //line sql.y:1235 + //line sql.y:1221 { yyVAL.str = EqualStr } - case 218: + case 215: yyDollar = yyS[yypt-1 : yypt+1] - //line sql.y:1239 + //line sql.y:1225 { yyVAL.str = LessThanStr } - case 219: + case 216: yyDollar = yyS[yypt-1 : yypt+1] - //line sql.y:1243 + //line sql.y:1229 { yyVAL.str = GreaterThanStr } - case 220: + case 217: yyDollar = yyS[yypt-1 : yypt+1] - //line sql.y:1247 + //line sql.y:1233 { yyVAL.str = LessEqualStr } - case 221: + case 218: yyDollar = yyS[yypt-1 : yypt+1] - //line sql.y:1251 + //line sql.y:1237 { yyVAL.str = GreaterEqualStr } - case 222: + case 219: yyDollar = yyS[yypt-1 : yypt+1] - //line sql.y:1255 + //line sql.y:1241 { yyVAL.str = NotEqualStr } - case 223: + case 220: yyDollar = yyS[yypt-1 : yypt+1] - //line sql.y:1259 + //line sql.y:1245 { yyVAL.str = NullSafeNotEqualStr } - case 224: + case 221: yyDollar = yyS[yypt-0 : yypt+1] - //line sql.y:1264 + //line sql.y:1250 { yyVAL.expr = nil } - case 225: + case 222: yyDollar = yyS[yypt-2 : yypt+1] - //line sql.y:1268 + //line sql.y:1254 { yyVAL.expr = yyDollar[2].expr } - case 226: + case 223: yyDollar = yyS[yypt-1 : yypt+1] - //line sql.y:1274 + //line sql.y:1260 { yyVAL.colTuple = yyDollar[1].valTuple } - case 227: + case 224: yyDollar = yyS[yypt-1 : yypt+1] - //line sql.y:1278 + //line sql.y:1264 { yyVAL.colTuple = yyDollar[1].subquery } - case 228: + case 225: yyDollar = yyS[yypt-1 : yypt+1] - //line sql.y:1282 + //line sql.y:1268 { yyVAL.colTuple = ListArg(yyDollar[1].bytes) } - case 229: + case 226: yyDollar = yyS[yypt-3 : yypt+1] - //line sql.y:1288 + //line sql.y:1274 { yyVAL.subquery = &Subquery{yyDollar[2].selStmt} } - case 230: + case 227: yyDollar = yyS[yypt-1 : yypt+1] - //line sql.y:1294 + //line sql.y:1280 { yyVAL.exprs = Exprs{yyDollar[1].expr} } - case 231: + case 228: yyDollar = yyS[yypt-3 : yypt+1] - //line sql.y:1298 + //line sql.y:1284 { yyVAL.exprs = append(yyDollar[1].exprs, yyDollar[3].expr) } - case 232: + case 229: yyDollar = yyS[yypt-1 : yypt+1] - //line sql.y:1304 + //line sql.y:1290 { yyVAL.expr = yyDollar[1].expr } - case 233: + case 230: yyDollar = yyS[yypt-1 : yypt+1] - //line sql.y:1308 + //line sql.y:1294 { yyVAL.expr = yyDollar[1].boolVal } - case 234: + case 231: yyDollar = yyS[yypt-1 : yypt+1] - //line sql.y:1312 + //line sql.y:1298 { yyVAL.expr = yyDollar[1].colName } - case 235: + case 232: yyDollar = yyS[yypt-1 : yypt+1] - //line sql.y:1316 + //line sql.y:1302 { yyVAL.expr = yyDollar[1].expr } - case 236: + case 233: yyDollar = yyS[yypt-1 : yypt+1] - //line sql.y:1320 + //line sql.y:1306 { yyVAL.expr = yyDollar[1].subquery } - case 237: + case 234: yyDollar = yyS[yypt-3 : yypt+1] - //line sql.y:1324 + //line sql.y:1310 { yyVAL.expr = &BinaryExpr{Left: yyDollar[1].expr, Operator: BitAndStr, Right: yyDollar[3].expr} } - case 238: + case 235: yyDollar = yyS[yypt-3 : yypt+1] - //line sql.y:1328 + //line sql.y:1314 { yyVAL.expr = &BinaryExpr{Left: yyDollar[1].expr, Operator: BitOrStr, Right: yyDollar[3].expr} } - case 239: + case 236: yyDollar = yyS[yypt-3 : yypt+1] - //line sql.y:1332 + //line sql.y:1318 { yyVAL.expr = &BinaryExpr{Left: yyDollar[1].expr, Operator: BitXorStr, Right: yyDollar[3].expr} } - case 240: + case 237: yyDollar = yyS[yypt-3 : yypt+1] - //line sql.y:1336 + //line sql.y:1322 { yyVAL.expr = &BinaryExpr{Left: yyDollar[1].expr, Operator: PlusStr, Right: yyDollar[3].expr} } - case 241: + case 238: yyDollar = yyS[yypt-3 : yypt+1] - //line sql.y:1340 + //line sql.y:1326 { yyVAL.expr = &BinaryExpr{Left: yyDollar[1].expr, Operator: MinusStr, Right: yyDollar[3].expr} } - case 242: + case 239: yyDollar = yyS[yypt-3 : yypt+1] - //line sql.y:1344 + //line sql.y:1330 { yyVAL.expr = &BinaryExpr{Left: yyDollar[1].expr, Operator: MultStr, Right: yyDollar[3].expr} } - case 243: + case 240: yyDollar = yyS[yypt-3 : yypt+1] - //line sql.y:1348 + //line sql.y:1334 { yyVAL.expr = &BinaryExpr{Left: yyDollar[1].expr, Operator: DivStr, Right: yyDollar[3].expr} } - case 244: + case 241: yyDollar = yyS[yypt-3 : yypt+1] - //line sql.y:1352 + //line sql.y:1338 { yyVAL.expr = &BinaryExpr{Left: yyDollar[1].expr, Operator: IntDivStr, Right: yyDollar[3].expr} } - case 245: + case 242: yyDollar = yyS[yypt-3 : yypt+1] - //line sql.y:1356 + //line sql.y:1342 { yyVAL.expr = &BinaryExpr{Left: yyDollar[1].expr, Operator: ModStr, Right: yyDollar[3].expr} } - case 246: + case 243: yyDollar = yyS[yypt-3 : yypt+1] - //line sql.y:1360 + //line sql.y:1346 { yyVAL.expr = &BinaryExpr{Left: yyDollar[1].expr, Operator: ModStr, Right: yyDollar[3].expr} } - case 247: + case 244: yyDollar = yyS[yypt-3 : yypt+1] - //line sql.y:1364 + //line sql.y:1350 { yyVAL.expr = &BinaryExpr{Left: yyDollar[1].expr, Operator: ShiftLeftStr, Right: yyDollar[3].expr} } - case 248: + case 245: yyDollar = yyS[yypt-3 : yypt+1] - //line sql.y:1368 + //line sql.y:1354 { yyVAL.expr = &BinaryExpr{Left: yyDollar[1].expr, Operator: ShiftRightStr, Right: yyDollar[3].expr} } - case 249: + case 246: yyDollar = yyS[yypt-2 : yypt+1] - //line sql.y:1372 + //line sql.y:1358 { if num, ok := yyDollar[2].expr.(*SQLVal); ok && num.Type == IntVal { yyVAL.expr = num @@ -3036,9 +3016,9 @@ yydefault: yyVAL.expr = &UnaryExpr{Operator: UPlusStr, Expr: yyDollar[2].expr} } } - case 250: + case 247: yyDollar = yyS[yypt-2 : yypt+1] - //line sql.y:1380 + //line sql.y:1366 { if num, ok := yyDollar[2].expr.(*SQLVal); ok && num.Type == IntVal { // Handle double negative @@ -3052,21 +3032,21 @@ yydefault: yyVAL.expr = &UnaryExpr{Operator: UMinusStr, Expr: yyDollar[2].expr} } } - case 251: + case 248: yyDollar = yyS[yypt-2 : yypt+1] - //line sql.y:1394 + //line sql.y:1380 { yyVAL.expr = &UnaryExpr{Operator: TildaStr, Expr: yyDollar[2].expr} } - case 252: + case 249: yyDollar = yyS[yypt-2 : yypt+1] - //line sql.y:1398 + //line sql.y:1384 { yyVAL.expr = &UnaryExpr{Operator: BangStr, Expr: yyDollar[2].expr} } - case 253: + case 250: yyDollar = yyS[yypt-3 : yypt+1] - //line sql.y:1402 + //line sql.y:1388 { // This rule prevents the usage of INTERVAL // as a function. If support is needed for that, @@ -3074,493 +3054,499 @@ yydefault: // will be non-trivial because of grammar conflicts. yyVAL.expr = &IntervalExpr{Expr: yyDollar[2].expr, Unit: yyDollar[3].colIdent.String()} } - case 258: + case 255: yyDollar = yyS[yypt-4 : yypt+1] - //line sql.y:1420 + //line sql.y:1406 { yyVAL.expr = &FuncExpr{Name: yyDollar[1].colIdent, Exprs: yyDollar[3].selectExprs} } - case 259: + case 256: yyDollar = yyS[yypt-5 : yypt+1] - //line sql.y:1424 + //line sql.y:1410 { yyVAL.expr = &FuncExpr{Name: yyDollar[1].colIdent, Distinct: true, Exprs: yyDollar[4].selectExprs} } - case 260: + case 257: yyDollar = yyS[yypt-6 : yypt+1] - //line sql.y:1428 + //line sql.y:1414 { yyVAL.expr = &FuncExpr{Qualifier: yyDollar[1].tableIdent, Name: yyDollar[3].colIdent, Exprs: yyDollar[5].selectExprs} } - case 261: + case 258: yyDollar = yyS[yypt-6 : yypt+1] - //line sql.y:1438 + //line sql.y:1424 { yyVAL.expr = &ConvertExpr{Expr: yyDollar[3].expr, Type: yyDollar[5].convertType} } - case 262: + case 259: yyDollar = yyS[yypt-7 : yypt+1] - //line sql.y:1442 + //line sql.y:1428 { yyVAL.expr = &GroupConcatExpr{Distinct: yyDollar[3].str, Exprs: yyDollar[4].selectExprs, OrderBy: yyDollar[5].orderBy, Separator: yyDollar[6].str} } - case 263: + case 260: yyDollar = yyS[yypt-5 : yypt+1] - //line sql.y:1446 + //line sql.y:1432 { yyVAL.expr = &CaseExpr{Expr: yyDollar[2].expr, Whens: yyDollar[3].whens, Else: yyDollar[4].expr} } - case 264: + case 261: yyDollar = yyS[yypt-4 : yypt+1] - //line sql.y:1450 + //line sql.y:1436 { yyVAL.expr = &ValuesFuncExpr{Name: yyDollar[3].colName} } - case 265: + case 262: yyDollar = yyS[yypt-1 : yypt+1] - //line sql.y:1460 + //line sql.y:1446 { yyVAL.expr = &TimeExpr{Expr: NewColIdent("current_timestamp")} } - case 266: + case 263: yyDollar = yyS[yypt-1 : yypt+1] - //line sql.y:1465 + //line sql.y:1451 { yyVAL.expr = &TimeExpr{Expr: NewColIdent("current_date")} } - case 267: + case 264: yyDollar = yyS[yypt-1 : yypt+1] - //line sql.y:1470 + //line sql.y:1456 { yyVAL.expr = &TimeExpr{Expr: NewColIdent("current_time")} } - case 268: + case 265: yyDollar = yyS[yypt-4 : yypt+1] - //line sql.y:1480 + //line sql.y:1466 { yyVAL.expr = &FuncExpr{Name: NewColIdent("if"), Exprs: yyDollar[3].selectExprs} } - case 269: + case 266: yyDollar = yyS[yypt-4 : yypt+1] - //line sql.y:1484 + //line sql.y:1470 { yyVAL.expr = &FuncExpr{Name: NewColIdent("mod"), Exprs: yyDollar[3].selectExprs} } - case 270: + case 267: yyDollar = yyS[yypt-4 : yypt+1] - //line sql.y:1488 + //line sql.y:1474 { yyVAL.expr = &FuncExpr{Name: NewColIdent("replace"), Exprs: yyDollar[3].selectExprs} } - case 271: + case 268: yyDollar = yyS[yypt-2 : yypt+1] - //line sql.y:1494 + //line sql.y:1480 { yyVAL.convertType = &ConvertType{Type: string(yyDollar[1].bytes), Length: yyDollar[2].optVal} } - case 272: + case 269: yyDollar = yyS[yypt-1 : yypt+1] - //line sql.y:1498 + //line sql.y:1484 { yyVAL.convertType = &ConvertType{Type: string(yyDollar[1].bytes)} } - case 273: + case 270: yyDollar = yyS[yypt-2 : yypt+1] - //line sql.y:1502 + //line sql.y:1488 { yyVAL.convertType = &ConvertType{Type: string(yyDollar[1].bytes), Length: yyDollar[2].optVal} } - case 274: + case 271: yyDollar = yyS[yypt-2 : yypt+1] - //line sql.y:1506 + //line sql.y:1492 { yyVAL.convertType = &ConvertType{Type: string(yyDollar[1].bytes)} yyVAL.convertType.Length = yyDollar[2].LengthScaleOption.Length yyVAL.convertType.Scale = yyDollar[2].LengthScaleOption.Scale } - case 275: + case 272: yyDollar = yyS[yypt-2 : yypt+1] - //line sql.y:1512 + //line sql.y:1498 { yyVAL.convertType = &ConvertType{Type: string(yyDollar[1].bytes), Length: yyDollar[2].optVal} } - case 276: + case 273: yyDollar = yyS[yypt-1 : yypt+1] - //line sql.y:1516 + //line sql.y:1502 { yyVAL.convertType = &ConvertType{Type: string(yyDollar[1].bytes)} } - case 277: + case 274: yyDollar = yyS[yypt-2 : yypt+1] - //line sql.y:1520 + //line sql.y:1506 { yyVAL.convertType = &ConvertType{Type: string(yyDollar[1].bytes)} } - case 278: + case 275: yyDollar = yyS[yypt-2 : yypt+1] - //line sql.y:1524 + //line sql.y:1510 { yyVAL.convertType = &ConvertType{Type: string(yyDollar[1].bytes), Length: yyDollar[2].optVal} } - case 279: + case 276: yyDollar = yyS[yypt-1 : yypt+1] - //line sql.y:1528 + //line sql.y:1514 { yyVAL.convertType = &ConvertType{Type: string(yyDollar[1].bytes)} } - case 280: + case 277: yyDollar = yyS[yypt-2 : yypt+1] - //line sql.y:1532 + //line sql.y:1518 { yyVAL.convertType = &ConvertType{Type: string(yyDollar[1].bytes)} } - case 281: + case 278: yyDollar = yyS[yypt-2 : yypt+1] - //line sql.y:1536 + //line sql.y:1522 { yyVAL.convertType = &ConvertType{Type: string(yyDollar[1].bytes), Length: yyDollar[2].optVal} } - case 282: + case 279: yyDollar = yyS[yypt-2 : yypt+1] - //line sql.y:1540 + //line sql.y:1526 { yyVAL.convertType = &ConvertType{Type: string(yyDollar[1].bytes), Length: yyDollar[2].optVal} } - case 283: + case 280: yyDollar = yyS[yypt-2 : yypt+1] - //line sql.y:1544 + //line sql.y:1530 { yyVAL.convertType = &ConvertType{Type: string(yyDollar[1].bytes), Length: yyDollar[2].optVal} } - case 284: + case 281: yyDollar = yyS[yypt-2 : yypt+1] - //line sql.y:1548 + //line sql.y:1534 { yyVAL.convertType = &ConvertType{Type: string(yyDollar[1].bytes), Length: yyDollar[2].optVal} } - case 285: + case 282: yyDollar = yyS[yypt-2 : yypt+1] - //line sql.y:1552 + //line sql.y:1538 { yyVAL.convertType = &ConvertType{Type: string(yyDollar[1].bytes), Length: yyDollar[2].optVal} } - case 286: + case 283: yyDollar = yyS[yypt-2 : yypt+1] - //line sql.y:1556 + //line sql.y:1542 { yyVAL.convertType = &ConvertType{Type: string(yyDollar[1].bytes), Length: yyDollar[2].optVal} } - case 287: + case 284: yyDollar = yyS[yypt-1 : yypt+1] - //line sql.y:1560 + //line sql.y:1546 { yyVAL.convertType = &ConvertType{Type: string(yyDollar[1].bytes)} } - case 288: + case 285: yyDollar = yyS[yypt-0 : yypt+1] - //line sql.y:1565 + //line sql.y:1551 { yyVAL.expr = nil } - case 289: + case 286: yyDollar = yyS[yypt-1 : yypt+1] - //line sql.y:1569 + //line sql.y:1555 { yyVAL.expr = yyDollar[1].expr } - case 290: + case 287: yyDollar = yyS[yypt-0 : yypt+1] - //line sql.y:1574 + //line sql.y:1560 { yyVAL.str = string("") } - case 291: + case 288: yyDollar = yyS[yypt-2 : yypt+1] - //line sql.y:1578 + //line sql.y:1564 { yyVAL.str = " separator '" + string(yyDollar[2].bytes) + "'" } - case 292: + case 289: yyDollar = yyS[yypt-1 : yypt+1] - //line sql.y:1584 + //line sql.y:1570 { yyVAL.whens = []*When{yyDollar[1].when} } - case 293: + case 290: yyDollar = yyS[yypt-2 : yypt+1] - //line sql.y:1588 + //line sql.y:1574 { yyVAL.whens = append(yyDollar[1].whens, yyDollar[2].when) } - case 294: + case 291: yyDollar = yyS[yypt-4 : yypt+1] - //line sql.y:1594 + //line sql.y:1580 { yyVAL.when = &When{Cond: yyDollar[2].expr, Val: yyDollar[4].expr} } - case 295: + case 292: yyDollar = yyS[yypt-0 : yypt+1] - //line sql.y:1599 + //line sql.y:1585 { yyVAL.expr = nil } - case 296: + case 293: yyDollar = yyS[yypt-2 : yypt+1] - //line sql.y:1603 + //line sql.y:1589 { yyVAL.expr = yyDollar[2].expr } - case 297: + case 294: yyDollar = yyS[yypt-1 : yypt+1] - //line sql.y:1609 + //line sql.y:1595 { yyVAL.colName = &ColName{Name: yyDollar[1].colIdent} } - case 298: + case 295: yyDollar = yyS[yypt-3 : yypt+1] - //line sql.y:1613 + //line sql.y:1599 { yyVAL.colName = &ColName{Qualifier: TableName{Name: yyDollar[1].tableIdent}, Name: yyDollar[3].colIdent} } - case 299: + case 296: yyDollar = yyS[yypt-5 : yypt+1] - //line sql.y:1617 + //line sql.y:1603 { yyVAL.colName = &ColName{Qualifier: TableName{Qualifier: yyDollar[1].tableIdent, Name: yyDollar[3].tableIdent}, Name: yyDollar[5].colIdent} } - case 300: + case 297: yyDollar = yyS[yypt-1 : yypt+1] - //line sql.y:1623 + //line sql.y:1609 { yyVAL.expr = NewStrVal(yyDollar[1].bytes) } - case 301: + case 298: yyDollar = yyS[yypt-1 : yypt+1] - //line sql.y:1627 + //line sql.y:1613 { yyVAL.expr = NewHexVal(yyDollar[1].bytes) } - case 302: + case 299: yyDollar = yyS[yypt-1 : yypt+1] - //line sql.y:1631 + //line sql.y:1617 { yyVAL.expr = NewIntVal(yyDollar[1].bytes) } - case 303: + case 300: yyDollar = yyS[yypt-1 : yypt+1] - //line sql.y:1635 + //line sql.y:1621 { yyVAL.expr = NewFloatVal(yyDollar[1].bytes) } - case 304: + case 301: yyDollar = yyS[yypt-1 : yypt+1] - //line sql.y:1639 + //line sql.y:1625 { yyVAL.expr = NewHexNum(yyDollar[1].bytes) } - case 305: + case 302: yyDollar = yyS[yypt-1 : yypt+1] - //line sql.y:1643 + //line sql.y:1629 { yyVAL.expr = NewValArg(yyDollar[1].bytes) } - case 306: + case 303: + yyDollar = yyS[yypt-1 : yypt+1] + //line sql.y:1633 + { + yyVAL.expr = NewPosArg(yyDollar[1].bytes) + } + case 304: yyDollar = yyS[yypt-1 : yypt+1] - //line sql.y:1647 + //line sql.y:1637 { yyVAL.expr = &NullVal{} } - case 307: + case 305: yyDollar = yyS[yypt-0 : yypt+1] - //line sql.y:1652 + //line sql.y:1642 { yyVAL.exprs = nil } - case 308: + case 306: yyDollar = yyS[yypt-3 : yypt+1] - //line sql.y:1656 + //line sql.y:1646 { yyVAL.exprs = yyDollar[3].exprs } - case 309: + case 307: yyDollar = yyS[yypt-0 : yypt+1] - //line sql.y:1661 + //line sql.y:1651 { yyVAL.expr = nil } - case 310: + case 308: yyDollar = yyS[yypt-2 : yypt+1] - //line sql.y:1665 + //line sql.y:1655 { yyVAL.expr = yyDollar[2].expr } - case 311: + case 309: yyDollar = yyS[yypt-0 : yypt+1] - //line sql.y:1670 + //line sql.y:1660 { yyVAL.orderBy = nil } - case 312: + case 310: yyDollar = yyS[yypt-3 : yypt+1] - //line sql.y:1674 + //line sql.y:1664 { yyVAL.orderBy = yyDollar[3].orderBy } - case 313: + case 311: yyDollar = yyS[yypt-1 : yypt+1] - //line sql.y:1680 + //line sql.y:1670 { yyVAL.orderBy = OrderBy{yyDollar[1].order} } - case 314: + case 312: yyDollar = yyS[yypt-3 : yypt+1] - //line sql.y:1684 + //line sql.y:1674 { yyVAL.orderBy = append(yyDollar[1].orderBy, yyDollar[3].order) } - case 315: + case 313: yyDollar = yyS[yypt-2 : yypt+1] - //line sql.y:1690 + //line sql.y:1680 { yyVAL.order = &Order{Expr: yyDollar[1].expr, Direction: yyDollar[2].str} } - case 316: + case 314: yyDollar = yyS[yypt-0 : yypt+1] - //line sql.y:1695 + //line sql.y:1685 { yyVAL.str = AscScr } - case 317: + case 315: yyDollar = yyS[yypt-1 : yypt+1] - //line sql.y:1699 + //line sql.y:1689 { yyVAL.str = AscScr } - case 318: + case 316: yyDollar = yyS[yypt-1 : yypt+1] - //line sql.y:1703 + //line sql.y:1693 { yyVAL.str = DescScr } - case 319: + case 317: yyDollar = yyS[yypt-0 : yypt+1] - //line sql.y:1708 + //line sql.y:1698 { yyVAL.limit = nil } - case 320: + case 318: yyDollar = yyS[yypt-2 : yypt+1] - //line sql.y:1712 + //line sql.y:1702 { yyVAL.limit = &Limit{Rowcount: yyDollar[2].expr} } - case 321: + case 319: yyDollar = yyS[yypt-4 : yypt+1] - //line sql.y:1716 + //line sql.y:1706 { yyVAL.limit = &Limit{Offset: yyDollar[2].expr, Rowcount: yyDollar[4].expr} } - case 322: + case 320: yyDollar = yyS[yypt-4 : yypt+1] - //line sql.y:1720 + //line sql.y:1710 { yyVAL.limit = &Limit{Offset: yyDollar[4].expr, Rowcount: yyDollar[2].expr} } - case 323: + case 321: yyDollar = yyS[yypt-2 : yypt+1] - //line sql.y:1733 + //line sql.y:1723 { yyVAL.ins = &Insert{Rows: yyDollar[2].values} } - case 324: + case 322: yyDollar = yyS[yypt-1 : yypt+1] - //line sql.y:1737 + //line sql.y:1727 { yyVAL.ins = &Insert{Rows: yyDollar[1].selStmt} } - case 325: + case 323: yyDollar = yyS[yypt-3 : yypt+1] - //line sql.y:1741 + //line sql.y:1731 { // Drop the redundant parenthesis. yyVAL.ins = &Insert{Rows: yyDollar[2].selStmt} } - case 326: + case 324: yyDollar = yyS[yypt-5 : yypt+1] - //line sql.y:1746 + //line sql.y:1736 { yyVAL.ins = &Insert{Columns: yyDollar[2].columns, Rows: yyDollar[5].values} } - case 327: + case 325: yyDollar = yyS[yypt-4 : yypt+1] - //line sql.y:1750 + //line sql.y:1740 { yyVAL.ins = &Insert{Columns: yyDollar[2].columns, Rows: yyDollar[4].selStmt} } - case 328: + case 326: yyDollar = yyS[yypt-6 : yypt+1] - //line sql.y:1754 + //line sql.y:1744 { // Drop the redundant parenthesis. yyVAL.ins = &Insert{Columns: yyDollar[2].columns, Rows: yyDollar[5].selStmt} } - case 329: + case 327: yyDollar = yyS[yypt-1 : yypt+1] - //line sql.y:1761 + //line sql.y:1751 { yyVAL.columns = Columns{yyDollar[1].colIdent} } - case 330: + case 328: yyDollar = yyS[yypt-3 : yypt+1] - //line sql.y:1765 + //line sql.y:1755 { yyVAL.columns = Columns{yyDollar[3].colIdent} } - case 331: + case 329: yyDollar = yyS[yypt-3 : yypt+1] - //line sql.y:1769 + //line sql.y:1759 { yyVAL.columns = append(yyVAL.columns, yyDollar[3].colIdent) } - case 332: + case 330: yyDollar = yyS[yypt-5 : yypt+1] - //line sql.y:1773 + //line sql.y:1763 { yyVAL.columns = append(yyVAL.columns, yyDollar[5].colIdent) } - case 333: + case 331: yyDollar = yyS[yypt-1 : yypt+1] - //line sql.y:1779 + //line sql.y:1769 { yyVAL.values = Values{yyDollar[1].valTuple} } - case 334: + case 332: yyDollar = yyS[yypt-3 : yypt+1] - //line sql.y:1783 + //line sql.y:1773 { yyVAL.values = append(yyDollar[1].values, yyDollar[3].valTuple) } - case 335: + case 333: yyDollar = yyS[yypt-1 : yypt+1] - //line sql.y:1789 + //line sql.y:1779 { yyVAL.valTuple = yyDollar[1].valTuple } - case 336: + case 334: yyDollar = yyS[yypt-2 : yypt+1] - //line sql.y:1793 + //line sql.y:1783 { yyVAL.valTuple = ValTuple{} } - case 337: + case 335: yyDollar = yyS[yypt-3 : yypt+1] - //line sql.y:1799 + //line sql.y:1789 { yyVAL.valTuple = ValTuple(yyDollar[2].exprs) } - case 338: + case 336: yyDollar = yyS[yypt-1 : yypt+1] - //line sql.y:1805 + //line sql.y:1795 { if len(yyDollar[1].valTuple) == 1 { yyVAL.expr = &ParenExpr{yyDollar[1].valTuple[0]} @@ -3568,150 +3554,150 @@ yydefault: yyVAL.expr = yyDollar[1].valTuple } } - case 339: + case 337: yyDollar = yyS[yypt-1 : yypt+1] - //line sql.y:1815 + //line sql.y:1805 { yyVAL.updateExprs = UpdateExprs{yyDollar[1].updateExpr} } - case 340: + case 338: yyDollar = yyS[yypt-3 : yypt+1] - //line sql.y:1819 + //line sql.y:1809 { yyVAL.updateExprs = append(yyDollar[1].updateExprs, yyDollar[3].updateExpr) } - case 341: + case 339: yyDollar = yyS[yypt-3 : yypt+1] - //line sql.y:1825 + //line sql.y:1815 { yyVAL.updateExpr = &UpdateExpr{Name: yyDollar[1].colName, Expr: yyDollar[3].expr} } - case 342: + case 340: yyDollar = yyS[yypt-0 : yypt+1] - //line sql.y:1830 + //line sql.y:1820 { yyVAL.byt = 0 } - case 343: + case 341: yyDollar = yyS[yypt-2 : yypt+1] - //line sql.y:1832 + //line sql.y:1822 { yyVAL.byt = 1 } - case 344: + case 342: yyDollar = yyS[yypt-0 : yypt+1] - //line sql.y:1835 + //line sql.y:1825 { yyVAL.empty = struct{}{} } - case 345: + case 343: yyDollar = yyS[yypt-3 : yypt+1] - //line sql.y:1837 + //line sql.y:1827 { yyVAL.empty = struct{}{} } - case 346: + case 344: yyDollar = yyS[yypt-0 : yypt+1] - //line sql.y:1840 + //line sql.y:1830 { yyVAL.str = "" } - case 347: + case 345: yyDollar = yyS[yypt-1 : yypt+1] - //line sql.y:1842 + //line sql.y:1832 { yyVAL.str = IgnoreStr } - case 348: + case 346: yyDollar = yyS[yypt-0 : yypt+1] - //line sql.y:1845 + //line sql.y:1835 { yyVAL.empty = struct{}{} } - case 349: + case 347: yyDollar = yyS[yypt-1 : yypt+1] - //line sql.y:1847 + //line sql.y:1837 { yyVAL.empty = struct{}{} } - case 350: + case 348: yyDollar = yyS[yypt-1 : yypt+1] - //line sql.y:1849 + //line sql.y:1839 { yyVAL.empty = struct{}{} } - case 351: + case 349: yyDollar = yyS[yypt-1 : yypt+1] - //line sql.y:1853 + //line sql.y:1843 { yyVAL.colIdent = NewColIdent(string(yyDollar[1].bytes)) } - case 352: + case 350: yyDollar = yyS[yypt-1 : yypt+1] - //line sql.y:1857 + //line sql.y:1847 { yyVAL.colIdent = NewColIdent(string(yyDollar[1].bytes)) } - case 354: + case 352: yyDollar = yyS[yypt-1 : yypt+1] - //line sql.y:1864 + //line sql.y:1854 { yyVAL.colIdent = NewColIdent(string(yyDollar[1].bytes)) } - case 355: + case 353: yyDollar = yyS[yypt-1 : yypt+1] - //line sql.y:1870 + //line sql.y:1860 { yyVAL.tableIdent = NewTableIdent(string(yyDollar[1].bytes)) } - case 356: + case 354: yyDollar = yyS[yypt-1 : yypt+1] - //line sql.y:1874 + //line sql.y:1864 { yyVAL.tableIdent = NewTableIdent(string(yyDollar[1].bytes)) } - case 358: + case 356: yyDollar = yyS[yypt-1 : yypt+1] - //line sql.y:1881 + //line sql.y:1871 { yyVAL.tableIdent = NewTableIdent(string(yyDollar[1].bytes)) } - case 466: + case 464: yyDollar = yyS[yypt-1 : yypt+1] - //line sql.y:2014 + //line sql.y:2004 { if incNesting(yylex) { yylex.Error("max nesting level reached") return 1 } } - case 467: + case 465: yyDollar = yyS[yypt-1 : yypt+1] - //line sql.y:2023 + //line sql.y:2013 { decNesting(yylex) } - case 468: + case 466: yyDollar = yyS[yypt-0 : yypt+1] - //line sql.y:2028 + //line sql.y:2018 { forceEOF(yylex) } - case 469: + case 467: yyDollar = yyS[yypt-0 : yypt+1] - //line sql.y:2033 + //line sql.y:2023 { forceEOF(yylex) } - case 470: + case 468: yyDollar = yyS[yypt-1 : yypt+1] - //line sql.y:2037 + //line sql.y:2027 { forceEOF(yylex) } - case 471: + case 469: yyDollar = yyS[yypt-1 : yypt+1] - //line sql.y:2041 + //line sql.y:2031 { forceEOF(yylex) } diff --git a/vendor/github.com/CovenantSQL/sqlparser/sql.y b/vendor/github.com/CovenantSQL/sqlparser/sql.y index ca86c1b74..37d4b0fe8 100644 --- a/vendor/github.com/CovenantSQL/sqlparser/sql.y +++ b/vendor/github.com/CovenantSQL/sqlparser/sql.y @@ -110,7 +110,7 @@ func forceEOF(yylex interface{}) { %left JOIN LEFT RIGHT INNER OUTER CROSS NATURAL %left ON USING %token '(' ',' ')' -%token ID HEX STRING INTEGRAL FLOAT HEXNUM VALUE_ARG LIST_ARG COMMENT +%token ID HEX STRING INTEGRAL FLOAT HEXNUM VALUE_ARG POS_ARG LIST_ARG COMMENT %token NULL TRUE FALSE %token FULL COLUMNS @@ -207,7 +207,7 @@ func forceEOF(yylex interface{}) { %type ins_column_list column_list %type update_list %type update_expression -%type ignore_opt default_opt +%type ignore_opt %type exists_opt %type not_exists_opt constraint_opt %type reserved_keyword non_reserved_keyword @@ -1137,20 +1137,6 @@ expression: { $$ = $1 } -| DEFAULT default_opt - { - $$ = &Default{ColName: $2} - } - -default_opt: - /* empty */ - { - $$ = "" - } -| openb ID closeb - { - $$ = string($2) - } boolean_value: TRUE @@ -1643,6 +1629,10 @@ value: { $$ = NewValArg($1) } +| POS_ARG + { + $$ = NewPosArg($1) + } | NULL { $$ = &NullVal{} diff --git a/vendor/github.com/CovenantSQL/sqlparser/token.go b/vendor/github.com/CovenantSQL/sqlparser/token.go index d357003da..36b556569 100644 --- a/vendor/github.com/CovenantSQL/sqlparser/token.go +++ b/vendor/github.com/CovenantSQL/sqlparser/token.go @@ -34,20 +34,21 @@ const ( // Tokenizer is the struct used to generate SQL // tokens for the parser. type Tokenizer struct { - InStream io.Reader - AllowComments bool - AllowBackSlashEscape bool - ForceEOF bool - lastChar uint16 - Position int - lastToken []byte - LastError error - posVarIndex int - ParseTree Statement - partialDDL *DDL - nesting int - multi bool - specialComment *Tokenizer + InStream io.Reader + AllowComments bool + AllowBackSlashEscape bool + SeparatePositionalArgs bool + ForceEOF bool + lastChar uint16 + Position int + lastToken []byte + LastError error + posVarIndex int + ParseTree Statement + partialDDL *DDL + nesting int + multi bool + specialComment *Tokenizer buf []byte bufPos int @@ -426,6 +427,9 @@ func (tkn *Tokenizer) Scan() (int, []byte) { tkn.posVarIndex++ buf := new(bytes2.Buffer) fmt.Fprintf(buf, ":v%d", tkn.posVarIndex) + if tkn.SeparatePositionalArgs { + return POS_ARG, buf.Bytes() + } return VALUE_ARG, buf.Bytes() case '.': if isDigit(tkn.lastChar) { diff --git a/vendor/github.com/miekg/dns/README.md b/vendor/github.com/miekg/dns/README.md index 7f1aaa5de..fd3a2f544 100644 --- a/vendor/github.com/miekg/dns/README.md +++ b/vendor/github.com/miekg/dns/README.md @@ -7,10 +7,10 @@ > Less is more. -Complete and usable DNS library. All widely used Resource Records are supported, including the -DNSSEC types. It follows a lean and mean philosophy. If there is stuff you should know as a DNS -programmer there isn't a convenience function for it. Server side and client side programming is -supported, i.e. you can build servers and resolvers with it. +Complete and usable DNS library. All Resource Records are supported, including the DNSSEC types. +It follows a lean and mean philosophy. If there is stuff you should know as a DNS programmer there +isn't a convenience function for it. Server side and client side programming is supported, i.e. you +can build servers and resolvers with it. We try to keep the "master" branch as sane as possible and at the bleeding edge of standards, avoiding breaking changes wherever reasonable. We support the last two versions of Go. @@ -42,10 +42,9 @@ A not-so-up-to-date-list-that-may-be-actually-current: * https://github.com/tianon/rawdns * https://mesosphere.github.io/mesos-dns/ * https://pulse.turbobytes.com/ -* https://play.google.com/store/apps/details?id=com.turbobytes.dig * https://github.com/fcambus/statzone * https://github.com/benschw/dns-clb-go -* https://github.com/corny/dnscheck for http://public-dns.info/ +* https://github.com/corny/dnscheck for * https://namesmith.io * https://github.com/miekg/unbound * https://github.com/miekg/exdns @@ -56,7 +55,7 @@ A not-so-up-to-date-list-that-may-be-actually-current: * https://github.com/bamarni/dockness * https://github.com/fffaraz/microdns * http://kelda.io -* https://github.com/ipdcode/hades (JD.COM) +* https://github.com/ipdcode/hades * https://github.com/StackExchange/dnscontrol/ * https://www.dnsperf.com/ * https://dnssectest.net/ @@ -73,24 +72,22 @@ Send pull request if you want to be listed here. # Features -* UDP/TCP queries, IPv4 and IPv6; -* RFC 1035 zone file parsing ($INCLUDE, $ORIGIN, $TTL and $GENERATE (for all record types) are supported; -* Fast: - * Reply speed around ~ 80K qps (faster hardware results in more qps); - * Parsing RRs ~ 100K RR/s, that's 5M records in about 50 seconds; -* Server side programming (mimicking the net/http package); -* Client side programming; -* DNSSEC: signing, validating and key generation for DSA, RSA, ECDSA and Ed25519; -* EDNS0, NSID, Cookies; -* AXFR/IXFR; -* TSIG, SIG(0); -* DNS over TLS: optional encrypted connection between client and server; -* DNS name compression; -* Depends only on the standard library. +* UDP/TCP queries, IPv4 and IPv6 +* RFC 1035 zone file parsing ($INCLUDE, $ORIGIN, $TTL and $GENERATE (for all record types) are supported +* Fast +* Server side programming (mimicking the net/http package) +* Client side programming +* DNSSEC: signing, validating and key generation for DSA, RSA, ECDSA and Ed25519 +* EDNS0, NSID, Cookies +* AXFR/IXFR +* TSIG, SIG(0) +* DNS over TLS (DoT): encrypted connection between client and server over TCP +* DNS name compression Have fun! Miek Gieben - 2010-2012 - +DNS Authors 2012- # Building @@ -164,9 +161,9 @@ Example programs can be found in the `github.com/miekg/exdns` repository. * 7873 - Domain Name System (DNS) Cookies (draft-ietf-dnsop-cookies) * 8080 - EdDSA for DNSSEC -## Loosely based upon +## Loosely Based Upon -* `ldns` -* `NSD` -* `Net::DNS` -* `GRONG` +* ldns - +* NSD - +* Net::DNS - +* GRONG - diff --git a/vendor/github.com/miekg/dns/acceptfunc.go b/vendor/github.com/miekg/dns/acceptfunc.go new file mode 100644 index 000000000..fcc6104f2 --- /dev/null +++ b/vendor/github.com/miekg/dns/acceptfunc.go @@ -0,0 +1,54 @@ +package dns + +// MsgAcceptFunc is used early in the server code to accept or reject a message with RcodeFormatError. +// It returns a MsgAcceptAction to indicate what should happen with the message. +type MsgAcceptFunc func(dh Header) MsgAcceptAction + +// DefaultMsgAcceptFunc checks the request and will reject if: +// +// * isn't a request (don't respond in that case). +// * opcode isn't OpcodeQuery or OpcodeNotify +// * Zero bit isn't zero +// * has more than 1 question in the question section +// * has more than 0 RRs in the Answer section +// * has more than 0 RRs in the Authority section +// * has more than 2 RRs in the Additional section +var DefaultMsgAcceptFunc MsgAcceptFunc = defaultMsgAcceptFunc + +// MsgAcceptAction represents the action to be taken. +type MsgAcceptAction int + +const ( + MsgAccept MsgAcceptAction = iota // Accept the message + MsgReject // Reject the message with a RcodeFormatError + MsgIgnore // Ignore the error and send nothing back. +) + +var defaultMsgAcceptFunc = func(dh Header) MsgAcceptAction { + if isResponse := dh.Bits&_QR != 0; isResponse { + return MsgIgnore + } + + // Don't allow dynamic updates, because then the sections can contain a whole bunch of RRs. + opcode := int(dh.Bits>>11) & 0xF + if opcode != OpcodeQuery && opcode != OpcodeNotify { + return MsgReject + } + + if isZero := dh.Bits&_Z != 0; isZero { + return MsgReject + } + if dh.Qdcount != 1 { + return MsgReject + } + if dh.Ancount != 0 { + return MsgReject + } + if dh.Nscount != 0 { + return MsgReject + } + if dh.Arcount > 2 { + return MsgReject + } + return MsgAccept +} diff --git a/vendor/github.com/miekg/dns/compress_generate.go b/vendor/github.com/miekg/dns/compress_generate.go deleted file mode 100644 index 9a136c414..000000000 --- a/vendor/github.com/miekg/dns/compress_generate.go +++ /dev/null @@ -1,198 +0,0 @@ -//+build ignore - -// compression_generate.go is meant to run with go generate. It will use -// go/{importer,types} to track down all the RR struct types. Then for each type -// it will look to see if there are (compressible) names, if so it will add that -// type to compressionLenHelperType and comressionLenSearchType which "fake" the -// compression so that Len() is fast. -package main - -import ( - "bytes" - "fmt" - "go/format" - "go/importer" - "go/types" - "log" - "os" -) - -var packageHdr = ` -// Code generated by "go run compress_generate.go"; DO NOT EDIT. - -package dns - -` - -// getTypeStruct will take a type and the package scope, and return the -// (innermost) struct if the type is considered a RR type (currently defined as -// those structs beginning with a RR_Header, could be redefined as implementing -// the RR interface). The bool return value indicates if embedded structs were -// resolved. -func getTypeStruct(t types.Type, scope *types.Scope) (*types.Struct, bool) { - st, ok := t.Underlying().(*types.Struct) - if !ok { - return nil, false - } - if st.Field(0).Type() == scope.Lookup("RR_Header").Type() { - return st, false - } - if st.Field(0).Anonymous() { - st, _ := getTypeStruct(st.Field(0).Type(), scope) - return st, true - } - return nil, false -} - -func main() { - // Import and type-check the package - pkg, err := importer.Default().Import("github.com/miekg/dns") - fatalIfErr(err) - scope := pkg.Scope() - - var domainTypes []string // Types that have a domain name in them (either compressible or not). - var cdomainTypes []string // Types that have a compressible domain name in them (subset of domainType) -Names: - for _, name := range scope.Names() { - o := scope.Lookup(name) - if o == nil || !o.Exported() { - continue - } - st, _ := getTypeStruct(o.Type(), scope) - if st == nil { - continue - } - if name == "PrivateRR" { - continue - } - - if scope.Lookup("Type"+o.Name()) == nil && o.Name() != "RFC3597" { - log.Fatalf("Constant Type%s does not exist.", o.Name()) - } - - for i := 1; i < st.NumFields(); i++ { - if _, ok := st.Field(i).Type().(*types.Slice); ok { - if st.Tag(i) == `dns:"domain-name"` { - domainTypes = append(domainTypes, o.Name()) - continue Names - } - if st.Tag(i) == `dns:"cdomain-name"` { - cdomainTypes = append(cdomainTypes, o.Name()) - domainTypes = append(domainTypes, o.Name()) - continue Names - } - continue - } - - switch { - case st.Tag(i) == `dns:"domain-name"`: - domainTypes = append(domainTypes, o.Name()) - continue Names - case st.Tag(i) == `dns:"cdomain-name"`: - cdomainTypes = append(cdomainTypes, o.Name()) - domainTypes = append(domainTypes, o.Name()) - continue Names - } - } - } - - b := &bytes.Buffer{} - b.WriteString(packageHdr) - - // compressionLenHelperType - all types that have domain-name/cdomain-name can be used for compressing names - - fmt.Fprint(b, "func compressionLenHelperType(c map[string]int, r RR, initLen int) int {\n") - fmt.Fprint(b, "currentLen := initLen\n") - fmt.Fprint(b, "switch x := r.(type) {\n") - for _, name := range domainTypes { - o := scope.Lookup(name) - st, _ := getTypeStruct(o.Type(), scope) - - fmt.Fprintf(b, "case *%s:\n", name) - for i := 1; i < st.NumFields(); i++ { - out := func(s string) { - fmt.Fprintf(b, "currentLen -= len(x.%s) + 1\n", st.Field(i).Name()) - fmt.Fprintf(b, "currentLen += compressionLenHelper(c, x.%s, currentLen)\n", st.Field(i).Name()) - } - - if _, ok := st.Field(i).Type().(*types.Slice); ok { - switch st.Tag(i) { - case `dns:"domain-name"`: - fallthrough - case `dns:"cdomain-name"`: - // For HIP we need to slice over the elements in this slice. - fmt.Fprintf(b, `for i := range x.%s { - currentLen -= len(x.%s[i]) + 1 -} -`, st.Field(i).Name(), st.Field(i).Name()) - fmt.Fprintf(b, `for i := range x.%s { - currentLen += compressionLenHelper(c, x.%s[i], currentLen) -} -`, st.Field(i).Name(), st.Field(i).Name()) - } - continue - } - - switch { - case st.Tag(i) == `dns:"cdomain-name"`: - fallthrough - case st.Tag(i) == `dns:"domain-name"`: - out(st.Field(i).Name()) - } - } - } - fmt.Fprintln(b, "}\nreturn currentLen - initLen\n}\n\n") - - // compressionLenSearchType - search cdomain-tags types for compressible names. - - fmt.Fprint(b, "func compressionLenSearchType(c map[string]int, r RR) (int, bool, int) {\n") - fmt.Fprint(b, "switch x := r.(type) {\n") - for _, name := range cdomainTypes { - o := scope.Lookup(name) - st, _ := getTypeStruct(o.Type(), scope) - - fmt.Fprintf(b, "case *%s:\n", name) - j := 1 - for i := 1; i < st.NumFields(); i++ { - out := func(s string, j int) { - fmt.Fprintf(b, "k%d, ok%d, sz%d := compressionLenSearch(c, x.%s)\n", j, j, j, st.Field(i).Name()) - } - - // There are no slice types with names that can be compressed. - - switch { - case st.Tag(i) == `dns:"cdomain-name"`: - out(st.Field(i).Name(), j) - j++ - } - } - k := "k1" - ok := "ok1" - sz := "sz1" - for i := 2; i < j; i++ { - k += fmt.Sprintf(" + k%d", i) - ok += fmt.Sprintf(" && ok%d", i) - sz += fmt.Sprintf(" + sz%d", i) - } - fmt.Fprintf(b, "return %s, %s, %s\n", k, ok, sz) - } - fmt.Fprintln(b, "}\nreturn 0, false, 0\n}\n\n") - - // gofmt - res, err := format.Source(b.Bytes()) - if err != nil { - b.WriteTo(os.Stderr) - log.Fatal(err) - } - - f, err := os.Create("zcompress.go") - fatalIfErr(err) - defer f.Close() - f.Write(res) -} - -func fatalIfErr(err error) { - if err != nil { - log.Fatal(err) - } -} diff --git a/vendor/github.com/miekg/dns/defaults.go b/vendor/github.com/miekg/dns/defaults.go index 14e18b0b3..1778b1cad 100644 --- a/vendor/github.com/miekg/dns/defaults.go +++ b/vendor/github.com/miekg/dns/defaults.go @@ -166,7 +166,7 @@ func (dns *Msg) IsEdns0() *OPT { // label fits in 63 characters, but there is no length check for the entire // string s. I.e. a domain name longer than 255 characters is considered valid. func IsDomainName(s string) (labels int, ok bool) { - _, labels, err := packDomainName(s, nil, 0, nil, false) + _, labels, err := packDomainName(s, nil, 0, compressionMap{}, false) return labels, err == nil } diff --git a/vendor/github.com/miekg/dns/dns.go b/vendor/github.com/miekg/dns/dns.go index e7557f51a..aefffa793 100644 --- a/vendor/github.com/miekg/dns/dns.go +++ b/vendor/github.com/miekg/dns/dns.go @@ -34,10 +34,15 @@ type RR interface { // copy returns a copy of the RR copy() RR - // len returns the length (in octets) of the uncompressed RR in wire format. - len() int + + // len returns the length (in octets) of the compressed or uncompressed RR in wire format. + // + // If compression is nil, the uncompressed size will be returned, otherwise the compressed + // size will be returned and domain names will be added to the map for future compression. + len(off int, compression map[string]struct{}) int + // pack packs an RR into wire format. - pack([]byte, int, map[string]int, bool) (int, error) + pack(msg []byte, off int, compression compressionMap, compress bool) (headerEnd int, off1 int, err error) } // RR_Header is the header all DNS resource records share. @@ -70,28 +75,29 @@ func (h *RR_Header) String() string { return s } -func (h *RR_Header) len() int { - l := len(h.Name) + 1 +func (h *RR_Header) len(off int, compression map[string]struct{}) int { + l := domainNameLen(h.Name, off, compression, true) l += 10 // rrtype(2) + class(2) + ttl(4) + rdlength(2) return l } // ToRFC3597 converts a known RR to the unknown RR representation from RFC 3597. func (rr *RFC3597) ToRFC3597(r RR) error { - buf := make([]byte, r.len()*2) - off, err := PackRR(r, buf, 0, nil, false) + buf := make([]byte, Len(r)*2) + headerEnd, off, err := packRR(r, buf, 0, compressionMap{}, false) if err != nil { return err } buf = buf[:off] - if int(r.Header().Rdlength) > off { - return ErrBuf - } - rfc3597, _, err := unpackRFC3597(*r.Header(), buf, off-int(r.Header().Rdlength)) + hdr := *r.Header() + hdr.Rdlength = uint16(off - headerEnd) + + rfc3597, _, err := unpackRFC3597(hdr, buf, headerEnd) if err != nil { return err } + *rr = *rfc3597.(*RFC3597) return nil } diff --git a/vendor/github.com/miekg/dns/dnssec.go b/vendor/github.com/miekg/dns/dnssec.go index 26b512e7d..9b39d4273 100644 --- a/vendor/github.com/miekg/dns/dnssec.go +++ b/vendor/github.com/miekg/dns/dnssec.go @@ -401,7 +401,7 @@ func (rr *RRSIG) Verify(k *DNSKEY, rrset []RR) error { if rr.Algorithm != k.Algorithm { return ErrKey } - if strings.ToLower(rr.SignerName) != strings.ToLower(k.Hdr.Name) { + if !strings.EqualFold(rr.SignerName, k.Hdr.Name) { return ErrKey } if k.Protocol != 3 { @@ -724,7 +724,7 @@ func rawSignatureData(rrset []RR, s *RRSIG) (buf []byte, err error) { x.Target = strings.ToLower(x.Target) } // 6.2. Canonical RR Form. (5) - origTTL - wire := make([]byte, r1.len()+1) // +1 to be safe(r) + wire := make([]byte, Len(r1)+1) // +1 to be safe(r) off, err1 := PackRR(r1, wire, 0, nil, false) if err1 != nil { return nil, err1 diff --git a/vendor/github.com/miekg/dns/doc.go b/vendor/github.com/miekg/dns/doc.go index 0389d7248..d3d7cec9e 100644 --- a/vendor/github.com/miekg/dns/doc.go +++ b/vendor/github.com/miekg/dns/doc.go @@ -1,20 +1,20 @@ /* Package dns implements a full featured interface to the Domain Name System. -Server- and client-side programming is supported. -The package allows complete control over what is sent out to the DNS. The package -API follows the less-is-more principle, by presenting a small, clean interface. +Both server- and client-side programming is supported. The package allows +complete control over what is sent out to the DNS. The API follows the +less-is-more principle, by presenting a small, clean interface. -The package dns supports (asynchronous) querying/replying, incoming/outgoing zone transfers, +It supports (asynchronous) querying/replying, incoming/outgoing zone transfers, TSIG, EDNS0, dynamic updates, notifies and DNSSEC validation/signing. -Note that domain names MUST be fully qualified, before sending them, unqualified + +Note that domain names MUST be fully qualified before sending them, unqualified names in a message will result in a packing failure. -Resource records are native types. They are not stored in wire format. -Basic usage pattern for creating a new resource record: +Resource records are native types. They are not stored in wire format. Basic +usage pattern for creating a new resource record: r := new(dns.MX) - r.Hdr = dns.RR_Header{Name: "miek.nl.", Rrtype: dns.TypeMX, - Class: dns.ClassINET, Ttl: 3600} + r.Hdr = dns.RR_Header{Name: "miek.nl.", Rrtype: dns.TypeMX, Class: dns.ClassINET, Ttl: 3600} r.Preference = 10 r.Mx = "mx.miek.nl." @@ -30,8 +30,8 @@ Or even: mx, err := dns.NewRR("$ORIGIN nl.\nmiek 1H IN MX 10 mx.miek") -In the DNS messages are exchanged, these messages contain resource -records (sets). Use pattern for creating a message: +In the DNS messages are exchanged, these messages contain resource records +(sets). Use pattern for creating a message: m := new(dns.Msg) m.SetQuestion("miek.nl.", dns.TypeMX) @@ -40,8 +40,8 @@ Or when not certain if the domain name is fully qualified: m.SetQuestion(dns.Fqdn("miek.nl"), dns.TypeMX) -The message m is now a message with the question section set to ask -the MX records for the miek.nl. zone. +The message m is now a message with the question section set to ask the MX +records for the miek.nl. zone. The following is slightly more verbose, but more flexible: @@ -51,9 +51,8 @@ The following is slightly more verbose, but more flexible: m1.Question = make([]dns.Question, 1) m1.Question[0] = dns.Question{"miek.nl.", dns.TypeMX, dns.ClassINET} -After creating a message it can be sent. -Basic use pattern for synchronous querying the DNS at a -server configured on 127.0.0.1 and port 53: +After creating a message it can be sent. Basic use pattern for synchronous +querying the DNS at a server configured on 127.0.0.1 and port 53: c := new(dns.Client) in, rtt, err := c.Exchange(m1, "127.0.0.1:53") @@ -99,25 +98,24 @@ the Answer section: Domain Name and TXT Character String Representations -Both domain names and TXT character strings are converted to presentation -form both when unpacked and when converted to strings. +Both domain names and TXT character strings are converted to presentation form +both when unpacked and when converted to strings. For TXT character strings, tabs, carriage returns and line feeds will be -converted to \t, \r and \n respectively. Back slashes and quotations marks -will be escaped. Bytes below 32 and above 127 will be converted to \DDD -form. +converted to \t, \r and \n respectively. Back slashes and quotations marks will +be escaped. Bytes below 32 and above 127 will be converted to \DDD form. -For domain names, in addition to the above rules brackets, periods, -spaces, semicolons and the at symbol are escaped. +For domain names, in addition to the above rules brackets, periods, spaces, +semicolons and the at symbol are escaped. DNSSEC -DNSSEC (DNS Security Extension) adds a layer of security to the DNS. It -uses public key cryptography to sign resource records. The -public keys are stored in DNSKEY records and the signatures in RRSIG records. +DNSSEC (DNS Security Extension) adds a layer of security to the DNS. It uses +public key cryptography to sign resource records. The public keys are stored in +DNSKEY records and the signatures in RRSIG records. -Requesting DNSSEC information for a zone is done by adding the DO (DNSSEC OK) bit -to a request. +Requesting DNSSEC information for a zone is done by adding the DO (DNSSEC OK) +bit to a request. m := new(dns.Msg) m.SetEdns0(4096, true) @@ -126,9 +124,9 @@ Signature generation, signature verification and key generation are all supporte DYNAMIC UPDATES -Dynamic updates reuses the DNS message format, but renames three of -the sections. Question is Zone, Answer is Prerequisite, Authority is -Update, only the Additional is not renamed. See RFC 2136 for the gory details. +Dynamic updates reuses the DNS message format, but renames three of the +sections. Question is Zone, Answer is Prerequisite, Authority is Update, only +the Additional is not renamed. See RFC 2136 for the gory details. You can set a rather complex set of rules for the existence of absence of certain resource records or names in a zone to specify if resource records @@ -145,10 +143,9 @@ DNS function shows which functions exist to specify the prerequisites. NONE rrset empty RRset does not exist dns.RRsetNotUsed zone rrset rr RRset exists (value dep) dns.Used -The prerequisite section can also be left empty. -If you have decided on the prerequisites you can tell what RRs should -be added or deleted. The next table shows the options you have and -what functions to call. +The prerequisite section can also be left empty. If you have decided on the +prerequisites you can tell what RRs should be added or deleted. The next table +shows the options you have and what functions to call. 3.4.2.6 - Table Of Metavalues Used In Update Section @@ -181,10 +178,10 @@ changes to the RRset after calling SetTsig() the signature will be incorrect. ... // When sending the TSIG RR is calculated and filled in before sending -When requesting an zone transfer (almost all TSIG usage is when requesting zone transfers), with -TSIG, this is the basic use pattern. In this example we request an AXFR for -miek.nl. with TSIG key named "axfr." and secret "so6ZGir4GPAqINNh9U5c3A==" -and using the server 176.58.119.54: +When requesting an zone transfer (almost all TSIG usage is when requesting zone +transfers), with TSIG, this is the basic use pattern. In this example we +request an AXFR for miek.nl. with TSIG key named "axfr." and secret +"so6ZGir4GPAqINNh9U5c3A==" and using the server 176.58.119.54: t := new(dns.Transfer) m := new(dns.Msg) @@ -194,8 +191,8 @@ and using the server 176.58.119.54: c, err := t.In(m, "176.58.119.54:53") for r := range c { ... } -You can now read the records from the transfer as they come in. Each envelope is checked with TSIG. -If something is not correct an error is returned. +You can now read the records from the transfer as they come in. Each envelope +is checked with TSIG. If something is not correct an error is returned. Basic use pattern validating and replying to a message that has TSIG set. @@ -220,29 +217,30 @@ Basic use pattern validating and replying to a message that has TSIG set. PRIVATE RRS -RFC 6895 sets aside a range of type codes for private use. This range -is 65,280 - 65,534 (0xFF00 - 0xFFFE). When experimenting with new Resource Records these +RFC 6895 sets aside a range of type codes for private use. This range is 65,280 +- 65,534 (0xFF00 - 0xFFFE). When experimenting with new Resource Records these can be used, before requesting an official type code from IANA. -see http://miek.nl/2014/September/21/idn-and-private-rr-in-go-dns/ for more +See https://miek.nl/2014/September/21/idn-and-private-rr-in-go-dns/ for more information. EDNS0 -EDNS0 is an extension mechanism for the DNS defined in RFC 2671 and updated -by RFC 6891. It defines an new RR type, the OPT RR, which is then completely +EDNS0 is an extension mechanism for the DNS defined in RFC 2671 and updated by +RFC 6891. It defines an new RR type, the OPT RR, which is then completely abused. + Basic use pattern for creating an (empty) OPT RR: o := new(dns.OPT) o.Hdr.Name = "." // MUST be the root zone, per definition. o.Hdr.Rrtype = dns.TypeOPT -The rdata of an OPT RR consists out of a slice of EDNS0 (RFC 6891) -interfaces. Currently only a few have been standardized: EDNS0_NSID -(RFC 5001) and EDNS0_SUBNET (draft-vandergaast-edns-client-subnet-02). Note -that these options may be combined in an OPT RR. -Basic use pattern for a server to check if (and which) options are set: +The rdata of an OPT RR consists out of a slice of EDNS0 (RFC 6891) interfaces. +Currently only a few have been standardized: EDNS0_NSID (RFC 5001) and +EDNS0_SUBNET (draft-vandergaast-edns-client-subnet-02). Note that these options +may be combined in an OPT RR. Basic use pattern for a server to check if (and +which) options are set: // o is a dns.OPT for _, s := range o.Option { @@ -262,10 +260,9 @@ From RFC 2931: ... protection for glue records, DNS requests, protection for message headers on requests and responses, and protection of the overall integrity of a response. -It works like TSIG, except that SIG(0) uses public key cryptography, instead of the shared -secret approach in TSIG. -Supported algorithms: DSA, ECDSAP256SHA256, ECDSAP384SHA384, RSASHA1, RSASHA256 and -RSASHA512. +It works like TSIG, except that SIG(0) uses public key cryptography, instead of +the shared secret approach in TSIG. Supported algorithms: DSA, ECDSAP256SHA256, +ECDSAP384SHA384, RSASHA1, RSASHA256 and RSASHA512. Signing subsequent messages in multi-message sessions is not implemented. */ diff --git a/vendor/github.com/miekg/dns/edns.go b/vendor/github.com/miekg/dns/edns.go index 18d054139..f8c60e616 100644 --- a/vendor/github.com/miekg/dns/edns.go +++ b/vendor/github.com/miekg/dns/edns.go @@ -78,8 +78,8 @@ func (rr *OPT) String() string { return s } -func (rr *OPT) len() int { - l := rr.Hdr.len() +func (rr *OPT) len(off int, compression map[string]struct{}) int { + l := rr.Hdr.len(off, compression) for i := 0; i < len(rr.Option); i++ { l += 4 // Account for 2-byte option code and 2-byte option length. lo, _ := rr.Option[i].pack() @@ -102,15 +102,14 @@ func (rr *OPT) SetVersion(v uint8) { // ExtendedRcode returns the EDNS extended RCODE field (the upper 8 bits of the TTL). func (rr *OPT) ExtendedRcode() int { - return int(rr.Hdr.Ttl&0xFF000000>>24) + 15 + return int(rr.Hdr.Ttl&0xFF000000>>24) << 4 } // SetExtendedRcode sets the EDNS extended RCODE field. -func (rr *OPT) SetExtendedRcode(v uint8) { - if v < RcodeBadVers { // Smaller than 16.. Use the 4 bits you have! - return - } - rr.Hdr.Ttl = rr.Hdr.Ttl&0x00FFFFFF | uint32(v-15)<<24 +// +// If the RCODE is not an extended RCODE, will reset the extended RCODE field to 0. +func (rr *OPT) SetExtendedRcode(v uint16) { + rr.Hdr.Ttl = rr.Hdr.Ttl&0x00FFFFFF | uint32(v>>4)<<24 } // UDPSize returns the UDP buffer size. @@ -274,22 +273,16 @@ func (e *EDNS0_SUBNET) unpack(b []byte) error { if e.SourceNetmask > net.IPv4len*8 || e.SourceScope > net.IPv4len*8 { return errors.New("dns: bad netmask") } - addr := make([]byte, net.IPv4len) - for i := 0; i < net.IPv4len && 4+i < len(b); i++ { - addr[i] = b[4+i] - } - e.Address = net.IPv4(addr[0], addr[1], addr[2], addr[3]) + addr := make(net.IP, net.IPv4len) + copy(addr, b[4:]) + e.Address = addr.To16() case 2: if e.SourceNetmask > net.IPv6len*8 || e.SourceScope > net.IPv6len*8 { return errors.New("dns: bad netmask") } - addr := make([]byte, net.IPv6len) - for i := 0; i < net.IPv6len && 4+i < len(b); i++ { - addr[i] = b[4+i] - } - e.Address = net.IP{addr[0], addr[1], addr[2], addr[3], addr[4], - addr[5], addr[6], addr[7], addr[8], addr[9], addr[10], - addr[11], addr[12], addr[13], addr[14], addr[15]} + addr := make(net.IP, net.IPv6len) + copy(addr, b[4:]) + e.Address = addr default: return errors.New("dns: bad address family") } diff --git a/vendor/github.com/miekg/dns/msg.go b/vendor/github.com/miekg/dns/msg.go index 47ac6cf28..33f14c489 100644 --- a/vendor/github.com/miekg/dns/msg.go +++ b/vendor/github.com/miekg/dns/msg.go @@ -9,7 +9,6 @@ package dns //go:generate go run msg_generate.go -//go:generate go run compress_generate.go import ( crand "crypto/rand" @@ -18,12 +17,35 @@ import ( "math/big" "math/rand" "strconv" + "strings" "sync" ) const ( maxCompressionOffset = 2 << 13 // We have 14 bits for the compression pointer maxDomainNameWireOctets = 255 // See RFC 1035 section 2.3.4 + + // This is the maximum number of compression pointers that should occur in a + // semantically valid message. Each label in a domain name must be at least one + // octet and is separated by a period. The root label won't be represented by a + // compression pointer to a compression pointer, hence the -2 to exclude the + // smallest valid root label. + // + // It is possible to construct a valid message that has more compression pointers + // than this, and still doesn't loop, by pointing to a previous pointer. This is + // not something a well written implementation should ever do, so we leave them + // to trip the maximum compression pointer check. + maxCompressionPointers = (maxDomainNameWireOctets+1)/2 - 2 + + // This is the maximum length of a domain name in presentation format. The + // maximum wire length of a domain name is 255 octets (see above), with the + // maximum label length being 63. The wire format requires one extra byte over + // the presentation format, reducing the number of octets by 1. Each label in + // the name will be separated by a single period, with each octet in the label + // expanding to at most 4 bytes (\DDD). If all other labels are of the maximum + // length, then the final label can only be 61 octets long to not exceed the + // maximum allowed wire length. + maxDomainNamePresentationLength = 61*4 + 1 + 63*4 + 1 + 63*4 + 1 + 63*4 + 1 ) // Errors defined in this package. @@ -46,10 +68,9 @@ var ( ErrRRset error = &Error{err: "bad rrset"} ErrSecret error = &Error{err: "no secrets defined"} ErrShortRead error = &Error{err: "short read"} - ErrSig error = &Error{err: "bad signature"} // ErrSig indicates that a signature can not be cryptographically validated. - ErrSoa error = &Error{err: "no SOA"} // ErrSOA indicates that no SOA RR was seen when doing zone transfers. - ErrTime error = &Error{err: "bad time"} // ErrTime indicates a timing error in TSIG authentication. - ErrTruncated error = &Error{err: "failed to unpack truncated message"} // ErrTruncated indicates that we failed to unpack a truncated message. We unpacked as much as we had so Msg can still be used, if desired. + ErrSig error = &Error{err: "bad signature"} // ErrSig indicates that a signature can not be cryptographically validated. + ErrSoa error = &Error{err: "no SOA"} // ErrSOA indicates that no SOA RR was seen when doing zone transfers. + ErrTime error = &Error{err: "bad time"} // ErrTime indicates a timing error in TSIG authentication. ) // Id by default, returns a 16 bits random number to be used as a @@ -151,7 +172,7 @@ var RcodeToString = map[int]string{ RcodeFormatError: "FORMERR", RcodeServerFailure: "SERVFAIL", RcodeNameError: "NXDOMAIN", - RcodeNotImplemented: "NOTIMPL", + RcodeNotImplemented: "NOTIMP", RcodeRefused: "REFUSED", RcodeYXDomain: "YXDOMAIN", // See RFC 2136 RcodeYXRrset: "YXRRSET", @@ -169,6 +190,39 @@ var RcodeToString = map[int]string{ RcodeBadCookie: "BADCOOKIE", } +// compressionMap is used to allow a more efficient compression map +// to be used for internal packDomainName calls without changing the +// signature or functionality of public API. +// +// In particular, map[string]uint16 uses 25% less per-entry memory +// than does map[string]int. +type compressionMap struct { + ext map[string]int // external callers + int map[string]uint16 // internal callers +} + +func (m compressionMap) valid() bool { + return m.int != nil || m.ext != nil +} + +func (m compressionMap) insert(s string, pos int) { + if m.ext != nil { + m.ext[s] = pos + } else { + m.int[s] = uint16(pos) + } +} + +func (m compressionMap) find(s string) (int, bool) { + if m.ext != nil { + pos, ok := m.ext[s] + return pos, ok + } + + pos, ok := m.int[s] + return int(pos), ok +} + // Domain names are a sequence of counted strings // split at the dots. They end with a zero-length string. @@ -177,149 +231,168 @@ var RcodeToString = map[int]string{ // map needs to hold a mapping between domain names and offsets // pointing into msg. func PackDomainName(s string, msg []byte, off int, compression map[string]int, compress bool) (off1 int, err error) { - off1, _, err = packDomainName(s, msg, off, compression, compress) + off1, _, err = packDomainName(s, msg, off, compressionMap{ext: compression}, compress) return } -func packDomainName(s string, msg []byte, off int, compression map[string]int, compress bool) (off1 int, labels int, err error) { +func packDomainName(s string, msg []byte, off int, compression compressionMap, compress bool) (off1 int, labels int, err error) { // special case if msg == nil lenmsg := 256 if msg != nil { lenmsg = len(msg) } + ls := len(s) if ls == 0 { // Ok, for instance when dealing with update RR without any rdata. return off, 0, nil } - // If not fully qualified, error out, but only if msg == nil #ugly - switch { - case msg == nil: - if s[ls-1] != '.' { - s += "." - ls++ - } - case msg != nil: - if s[ls-1] != '.' { + + // If not fully qualified, error out, but only if msg != nil #ugly + if s[ls-1] != '.' { + if msg != nil { return lenmsg, 0, ErrFqdn } + s += "." + ls++ } + // Each dot ends a segment of the name. // We trade each dot byte for a length byte. // Except for escaped dots (\.), which are normal dots. // There is also a trailing zero. // Compression - nameoffset := -1 pointer := -1 + // Emit sequence of counted strings, chopping at dots. - begin := 0 - bs := []byte(s) - roBs, bsFresh, escapedDot := s, true, false + var ( + begin int + compBegin int + compOff int + bs []byte + wasDot bool + ) +loop: for i := 0; i < ls; i++ { - if bs[i] == '\\' { - for j := i; j < ls-1; j++ { - bs[j] = bs[j+1] - } - ls-- + var c byte + if bs == nil { + c = s[i] + } else { + c = bs[i] + } + + switch c { + case '\\': if off+1 > lenmsg { return lenmsg, labels, ErrBuf } + + if bs == nil { + bs = []byte(s) + } + // check for \DDD - if i+2 < ls && isDigit(bs[i]) && isDigit(bs[i+1]) && isDigit(bs[i+2]) { - bs[i] = dddToByte(bs[i:]) - for j := i + 1; j < ls-2; j++ { - bs[j] = bs[j+2] - } - ls -= 2 + if i+3 < ls && isDigit(bs[i+1]) && isDigit(bs[i+2]) && isDigit(bs[i+3]) { + bs[i] = dddToByte(bs[i+1:]) + copy(bs[i+1:ls-3], bs[i+4:]) + ls -= 3 + compOff += 3 + } else { + copy(bs[i:ls-1], bs[i+1:]) + ls-- + compOff++ } - escapedDot = bs[i] == '.' - bsFresh = false - continue - } - if bs[i] == '.' { - if i > 0 && bs[i-1] == '.' && !escapedDot { + wasDot = false + case '.': + if wasDot { // two dots back to back is not legal return lenmsg, labels, ErrRdata } - if i-begin >= 1<<6 { // top two bits of length must be clear + wasDot = true + + labelLen := i - begin + if labelLen >= 1<<6 { // top two bits of length must be clear return lenmsg, labels, ErrRdata } + // off can already (we're in a loop) be bigger than len(msg) // this happens when a name isn't fully qualified - if off+1 > lenmsg { + if off+1+labelLen > lenmsg { return lenmsg, labels, ErrBuf } - if msg != nil { - msg[off] = byte(i - begin) - } - offset := off - off++ - for j := begin; j < i; j++ { - if off+1 > lenmsg { - return lenmsg, labels, ErrBuf - } - if msg != nil { - msg[off] = bs[j] - } - off++ - } - if compress && !bsFresh { - roBs = string(bs) - bsFresh = true - } + // Don't try to compress '.' - // We should only compress when compress it true, but we should also still pick + // We should only compress when compress is true, but we should also still pick // up names that can be used for *future* compression(s). - if compression != nil && roBs[begin:] != "." { - if p, ok := compression[roBs[begin:]]; !ok { - // Only offsets smaller than this can be used. - if offset < maxCompressionOffset { - compression[roBs[begin:]] = offset - } - } else { + if compression.valid() && !isRootLabel(s, bs, begin, ls) { + if p, ok := compression.find(s[compBegin:]); ok { // The first hit is the longest matching dname // keep the pointer offset we get back and store // the offset of the current name, because that's // where we need to insert the pointer later // If compress is true, we're allowed to compress this dname - if pointer == -1 && compress { - pointer = p // Where to point to - nameoffset = offset // Where to point from - break + if compress { + pointer = p // Where to point to + break loop } + } else if off < maxCompressionOffset { + // Only offsets smaller than maxCompressionOffset can be used. + compression.insert(s[compBegin:], off) + } + } + + // The following is covered by the length check above. + if msg != nil { + msg[off] = byte(labelLen) + + if bs == nil { + copy(msg[off+1:], s[begin:i]) + } else { + copy(msg[off+1:], bs[begin:i]) } } + off += 1 + labelLen + labels++ begin = i + 1 + compBegin = begin + compOff + default: + wasDot = false } - escapedDot = false } + // Root label is special - if len(bs) == 1 && bs[0] == '.' { + if isRootLabel(s, bs, 0, ls) { return off, labels, nil } + // If we did compression and we find something add the pointer here if pointer != -1 { - // Clear the msg buffer after the pointer location, otherwise - // packDataNsec writes the wrong data to msg. - tainted := msg[nameoffset:off] - for i := range tainted { - tainted[i] = 0 - } // We have two bytes (14 bits) to put the pointer in // if msg == nil, we will never do compression - binary.BigEndian.PutUint16(msg[nameoffset:], uint16(pointer^0xC000)) - off = nameoffset + 1 - goto End + binary.BigEndian.PutUint16(msg[off:], uint16(pointer^0xC000)) + return off + 2, labels, nil } - if msg != nil && off < len(msg) { + + if msg != nil && off < lenmsg { msg[off] = 0 } -End: - off++ - return off, labels, nil + + return off + 1, labels, nil +} + +// isRootLabel returns whether s or bs, from off to end, is the root +// label ".". +// +// If bs is nil, s will be checked, otherwise bs will be checked. +func isRootLabel(s string, bs []byte, off, end int) bool { + if bs == nil { + return s[off:end] == "." + } + + return end-off == 1 && bs[off] == '.' } // Unpack a domain name. @@ -336,12 +409,16 @@ End: // In theory, the pointers are only allowed to jump backward. // We let them jump anywhere and stop jumping after a while. -// UnpackDomainName unpacks a domain name into a string. +// UnpackDomainName unpacks a domain name into a string. It returns +// the name, the new offset into msg and any error that occurred. +// +// When an error is encountered, the unpacked name will be discarded +// and len(msg) will be returned as the offset. func UnpackDomainName(msg []byte, off int) (string, int, error) { - s := make([]byte, 0, 64) + s := make([]byte, 0, maxDomainNamePresentationLength) off1 := 0 lenmsg := len(msg) - maxLen := maxDomainNameWireOctets + budget := maxDomainNameWireOctets ptr := 0 // number of pointers followed Loop: for { @@ -360,25 +437,19 @@ Loop: if off+c > lenmsg { return "", lenmsg, ErrBuf } + budget -= c + 1 // +1 for the label separator + if budget <= 0 { + return "", lenmsg, ErrLongDomain + } for j := off; j < off+c; j++ { switch b := msg[j]; b { case '.', '(', ')', ';', ' ', '@': fallthrough case '"', '\\': s = append(s, '\\', b) - // presentation-format \X escapes add an extra byte - maxLen++ default: - if b < 32 || b >= 127 { // unprintable, use \DDD - var buf [3]byte - bufs := strconv.AppendInt(buf[:0], int64(b), 10) - s = append(s, '\\') - for i := len(bufs); i < 3; i++ { - s = append(s, '0') - } - s = append(s, bufs...) - // presentation-format \DDD escapes add 3 extra bytes - maxLen += 3 + if b < ' ' || b > '~' { // unprintable, use \DDD + s = append(s, escapeByte(b)...) } else { s = append(s, b) } @@ -400,7 +471,7 @@ Loop: if ptr == 0 { off1 = off } - if ptr++; ptr > 10 { + if ptr++; ptr > maxCompressionPointers { return "", lenmsg, &Error{err: "too many compression pointers"} } // pointer should guarantee that it advances and points forwards at least @@ -416,10 +487,7 @@ Loop: off1 = off } if len(s) == 0 { - s = []byte(".") - } else if len(s) >= maxLen { - // error if the name is too long, but don't throw it away - return string(s), lenmsg, ErrLongDomain + return ".", off1, nil } return string(s), off1, nil } @@ -528,10 +596,12 @@ func unpackTxt(msg []byte, off0 int) (ss []string, off int, err error) { func isDigit(b byte) bool { return b >= '0' && b <= '9' } func dddToByte(s []byte) byte { + _ = s[2] // bounds check hint to compiler; see golang.org/issue/14808 return byte((s[0]-'0')*100 + (s[1]-'0')*10 + (s[2] - '0')) } func dddStringToByte(s string) byte { + _ = s[2] // bounds check hint to compiler; see golang.org/issue/14808 return byte((s[0]-'0')*100 + (s[1]-'0')*10 + (s[2] - '0')) } @@ -549,19 +619,33 @@ func intToBytes(i *big.Int, length int) []byte { // PackRR packs a resource record rr into msg[off:]. // See PackDomainName for documentation about the compression. func PackRR(rr RR, msg []byte, off int, compression map[string]int, compress bool) (off1 int, err error) { + headerEnd, off1, err := packRR(rr, msg, off, compressionMap{ext: compression}, compress) + if err == nil { + // packRR no longer sets the Rdlength field on the rr, but + // callers might be expecting it so we set it here. + rr.Header().Rdlength = uint16(off1 - headerEnd) + } + return off1, err +} + +func packRR(rr RR, msg []byte, off int, compression compressionMap, compress bool) (headerEnd int, off1 int, err error) { if rr == nil { - return len(msg), &Error{err: "nil rr"} + return len(msg), len(msg), &Error{err: "nil rr"} } - off1, err = rr.pack(msg, off, compression, compress) + headerEnd, off1, err = rr.pack(msg, off, compression, compress) if err != nil { - return len(msg), err + return headerEnd, len(msg), err } - // TODO(miek): Not sure if this is needed? If removed we can remove rawmsg.go as well. - if rawSetRdlength(msg, off, off1) { - return off1, nil + + rdlength := off1 - headerEnd + if int(uint16(rdlength)) != rdlength { // overflow + return headerEnd, len(msg), ErrRdata } - return off, ErrRdata + + // The RDLENGTH field is the last field in the header and we set it here. + binary.BigEndian.PutUint16(msg[headerEnd-2:], uint16(rdlength)) + return headerEnd, off1, nil } // UnpackRR unpacks msg[off:] into an RR. @@ -668,32 +752,33 @@ func (dns *Msg) Pack() (msg []byte, err error) { // PackBuffer packs a Msg, using the given buffer buf. If buf is too small a new buffer is allocated. func (dns *Msg) PackBuffer(buf []byte) (msg []byte, err error) { - var compression map[string]int - if dns.Compress { - compression = make(map[string]int) // Compression pointer mappings. + // If this message can't be compressed, avoid filling the + // compression map and creating garbage. + if dns.Compress && dns.isCompressible() { + compression := make(map[string]uint16) // Compression pointer mappings. + return dns.packBufferWithCompressionMap(buf, compressionMap{int: compression}, true) } - return dns.packBufferWithCompressionMap(buf, compression) + + return dns.packBufferWithCompressionMap(buf, compressionMap{}, false) } // packBufferWithCompressionMap packs a Msg, using the given buffer buf. -func (dns *Msg) packBufferWithCompressionMap(buf []byte, compression map[string]int) (msg []byte, err error) { - // We use a similar function in tsig.go's stripTsig. - - var dh Header - +func (dns *Msg) packBufferWithCompressionMap(buf []byte, compression compressionMap, compress bool) (msg []byte, err error) { if dns.Rcode < 0 || dns.Rcode > 0xFFF { return nil, ErrRcode } - if dns.Rcode > 0xF { - // Regular RCODE field is 4 bits - opt := dns.IsEdns0() - if opt == nil { - return nil, ErrExtendedRcode - } - opt.SetExtendedRcode(uint8(dns.Rcode >> 4)) + + // Set extended rcode unconditionally if we have an opt, this will allow + // reseting the extended rcode bits if they need to. + if opt := dns.IsEdns0(); opt != nil { + opt.SetExtendedRcode(uint16(dns.Rcode)) + } else if dns.Rcode > 0xF { + // If Rcode is an extended one and opt is nil, error out. + return nil, ErrExtendedRcode } // Convert convenient Msg into wire-like Header. + var dh Header dh.Id = dns.Id dh.Bits = uint16(dns.Opcode)<<11 | uint16(dns.Rcode&0xF) if dns.Response { @@ -721,50 +806,44 @@ func (dns *Msg) packBufferWithCompressionMap(buf []byte, compression map[string] dh.Bits |= _CD } - // Prepare variable sized arrays. - question := dns.Question - answer := dns.Answer - ns := dns.Ns - extra := dns.Extra - - dh.Qdcount = uint16(len(question)) - dh.Ancount = uint16(len(answer)) - dh.Nscount = uint16(len(ns)) - dh.Arcount = uint16(len(extra)) + dh.Qdcount = uint16(len(dns.Question)) + dh.Ancount = uint16(len(dns.Answer)) + dh.Nscount = uint16(len(dns.Ns)) + dh.Arcount = uint16(len(dns.Extra)) // We need the uncompressed length here, because we first pack it and then compress it. msg = buf - uncompressedLen := compressedLen(dns, false) + uncompressedLen := msgLenWithCompressionMap(dns, nil) if packLen := uncompressedLen + 1; len(msg) < packLen { msg = make([]byte, packLen) } // Pack it in: header and then the pieces. off := 0 - off, err = dh.pack(msg, off, compression, dns.Compress) + off, err = dh.pack(msg, off, compression, compress) if err != nil { return nil, err } - for i := 0; i < len(question); i++ { - off, err = question[i].pack(msg, off, compression, dns.Compress) + for _, r := range dns.Question { + off, err = r.pack(msg, off, compression, compress) if err != nil { return nil, err } } - for i := 0; i < len(answer); i++ { - off, err = PackRR(answer[i], msg, off, compression, dns.Compress) + for _, r := range dns.Answer { + _, off, err = packRR(r, msg, off, compression, compress) if err != nil { return nil, err } } - for i := 0; i < len(ns); i++ { - off, err = PackRR(ns[i], msg, off, compression, dns.Compress) + for _, r := range dns.Ns { + _, off, err = packRR(r, msg, off, compression, compress) if err != nil { return nil, err } } - for i := 0; i < len(extra); i++ { - off, err = PackRR(extra[i], msg, off, compression, dns.Compress) + for _, r := range dns.Extra { + _, off, err = packRR(r, msg, off, compression, compress) if err != nil { return nil, err } @@ -772,28 +851,7 @@ func (dns *Msg) packBufferWithCompressionMap(buf []byte, compression map[string] return msg[:off], nil } -// Unpack unpacks a binary message to a Msg structure. -func (dns *Msg) Unpack(msg []byte) (err error) { - var ( - dh Header - off int - ) - if dh, off, err = unpackMsgHdr(msg, off); err != nil { - return err - } - - dns.Id = dh.Id - dns.Response = dh.Bits&_QR != 0 - dns.Opcode = int(dh.Bits>>11) & 0xF - dns.Authoritative = dh.Bits&_AA != 0 - dns.Truncated = dh.Bits&_TC != 0 - dns.RecursionDesired = dh.Bits&_RD != 0 - dns.RecursionAvailable = dh.Bits&_RA != 0 - dns.Zero = dh.Bits&_Z != 0 - dns.AuthenticatedData = dh.Bits&_AD != 0 - dns.CheckingDisabled = dh.Bits&_CD != 0 - dns.Rcode = int(dh.Bits & 0xF) - +func (dns *Msg) unpack(dh Header, msg []byte, off int) (err error) { // If we are at the end of the message we should return *just* the // header. This can still be useful to the caller. 9.9.9.9 sends these // when responding with REFUSED for instance. @@ -812,8 +870,6 @@ func (dns *Msg) Unpack(msg []byte) (err error) { var q Question q, off, err = unpackQuestion(msg, off) if err != nil { - // Even if Truncated is set, we only will set ErrTruncated if we - // actually got the questions return err } if off1 == off { // Offset does not increase anymore, dh.Qdcount is a lie! @@ -837,16 +893,29 @@ func (dns *Msg) Unpack(msg []byte) (err error) { // The header counts might have been wrong so we need to update it dh.Arcount = uint16(len(dns.Extra)) + // Set extended Rcode + if opt := dns.IsEdns0(); opt != nil { + dns.Rcode |= opt.ExtendedRcode() + } + if off != len(msg) { // TODO(miek) make this an error? // use PackOpt to let people tell how detailed the error reporting should be? // println("dns: extra bytes in dns packet", off, "<", len(msg)) - } else if dns.Truncated { - // Whether we ran into a an error or not, we want to return that it - // was truncated - err = ErrTruncated } return err + +} + +// Unpack unpacks a binary message to a Msg structure. +func (dns *Msg) Unpack(msg []byte) (err error) { + dh, off, err := unpackMsgHdr(msg, 0) + if err != nil { + return err + } + + dns.setHdr(dh) + return dns.unpack(dh, msg, off) } // Convert a complete message to a string with dig-like output. @@ -892,151 +961,117 @@ func (dns *Msg) String() string { return s } +// isCompressible returns whether the msg may be compressible. +func (dns *Msg) isCompressible() bool { + // If we only have one question, there is nothing we can ever compress. + return len(dns.Question) > 1 || len(dns.Answer) > 0 || + len(dns.Ns) > 0 || len(dns.Extra) > 0 +} + // Len returns the message length when in (un)compressed wire format. // If dns.Compress is true compression it is taken into account. Len() // is provided to be a faster way to get the size of the resulting packet, // than packing it, measuring the size and discarding the buffer. -func (dns *Msg) Len() int { return compressedLen(dns, dns.Compress) } - -func compressedLenWithCompressionMap(dns *Msg, compression map[string]int) int { - l := 12 // Message header is always 12 bytes - for _, r := range dns.Question { - compressionLenHelper(compression, r.Name, l) - l += r.len() +func (dns *Msg) Len() int { + // If this message can't be compressed, avoid filling the + // compression map and creating garbage. + if dns.Compress && dns.isCompressible() { + compression := make(map[string]struct{}) + return msgLenWithCompressionMap(dns, compression) } - l += compressionLenSlice(l, compression, dns.Answer) - l += compressionLenSlice(l, compression, dns.Ns) - l += compressionLenSlice(l, compression, dns.Extra) - return l + + return msgLenWithCompressionMap(dns, nil) } -// compressedLen returns the message length when in compressed wire format -// when compress is true, otherwise the uncompressed length is returned. -func compressedLen(dns *Msg, compress bool) int { - // We always return one more than needed. - if compress { - compression := map[string]int{} - return compressedLenWithCompressionMap(dns, compression) - } +func msgLenWithCompressionMap(dns *Msg, compression map[string]struct{}) int { l := 12 // Message header is always 12 bytes for _, r := range dns.Question { - l += r.len() + l += r.len(l, compression) } for _, r := range dns.Answer { if r != nil { - l += r.len() + l += r.len(l, compression) } } for _, r := range dns.Ns { if r != nil { - l += r.len() + l += r.len(l, compression) } } for _, r := range dns.Extra { if r != nil { - l += r.len() + l += r.len(l, compression) } } return l } -func compressionLenSlice(lenp int, c map[string]int, rs []RR) int { - initLen := lenp - for _, r := range rs { - if r == nil { - continue - } - // TmpLen is to track len of record at 14bits boudaries - tmpLen := lenp - - x := r.len() - // track this length, and the global length in len, while taking compression into account for both. - k, ok, _ := compressionLenSearch(c, r.Header().Name) - if ok { - // Size of x is reduced by k, but we add 1 since k includes the '.' and label descriptor take 2 bytes - // so, basically x:= x - k - 1 + 2 - x += 1 - k - } +func domainNameLen(s string, off int, compression map[string]struct{}, compress bool) int { + if s == "" || s == "." { + return 1 + } - tmpLen += compressionLenHelper(c, r.Header().Name, tmpLen) - k, ok, _ = compressionLenSearchType(c, r) - if ok { - x += 1 - k + escaped := strings.Contains(s, "\\") + + if compression != nil && (compress || off < maxCompressionOffset) { + // compressionLenSearch will insert the entry into the compression + // map if it doesn't contain it. + if l, ok := compressionLenSearch(compression, s, off); ok && compress { + if escaped { + return escapedNameLen(s[:l]) + 2 + } + + return l + 2 } - lenp += x - tmpLen = lenp - tmpLen += compressionLenHelperType(c, r, tmpLen) + } + if escaped { + return escapedNameLen(s) + 1 } - return lenp - initLen + + return len(s) + 1 } -// Put the parts of the name in the compression map, return the size in bytes added in payload -func compressionLenHelper(c map[string]int, s string, currentLen int) int { - if currentLen > maxCompressionOffset { - // We won't be able to add any label that could be re-used later anyway - return 0 - } - if _, ok := c[s]; ok { - return 0 - } - initLen := currentLen - pref := "" - prev := s - lbs := Split(s) - for j := 0; j < len(lbs); j++ { - pref = s[lbs[j]:] - currentLen += len(prev) - len(pref) - prev = pref - if _, ok := c[pref]; !ok { - // If first byte label is within the first 14bits, it might be re-used later - if currentLen < maxCompressionOffset { - c[pref] = currentLen - } +func escapedNameLen(s string) int { + nameLen := len(s) + for i := 0; i < len(s); i++ { + if s[i] != '\\' { + continue + } + + if i+3 < len(s) && isDigit(s[i+1]) && isDigit(s[i+2]) && isDigit(s[i+3]) { + nameLen -= 3 + i += 3 } else { - added := currentLen - initLen - if j > 0 { - // We added a new PTR - added += 2 - } - return added + nameLen-- + i++ } } - return currentLen - initLen + + return nameLen } -// Look for each part in the compression map and returns its length, -// keep on searching so we get the longest match. -// Will return the size of compression found, whether a match has been -// found and the size of record if added in payload -func compressionLenSearch(c map[string]int, s string) (int, bool, int) { - off := 0 - end := false - if s == "" { // don't bork on bogus data - return 0, false, 0 - } - fullSize := 0 - for { +func compressionLenSearch(c map[string]struct{}, s string, msgOff int) (int, bool) { + for off, end := 0, false; !end; off, end = NextLabel(s, off) { if _, ok := c[s[off:]]; ok { - return len(s[off:]), true, fullSize + off + return off, true } - if end { - break + + if msgOff+off < maxCompressionOffset { + c[s[off:]] = struct{}{} } - // Each label descriptor takes 2 bytes, add it - fullSize += 2 - off, end = NextLabel(s, off) } - return 0, false, fullSize + len(s) + + return 0, false } // Copy returns a new RR which is a deep-copy of r. func Copy(r RR) RR { r1 := r.copy(); return r1 } // Len returns the length (in octets) of the uncompressed RR in wire format. -func Len(r RR) int { return r.len() } +func Len(r RR) int { return r.len(0, nil) } // Copy returns a new *Msg which is a deep-copy of dns. func (dns *Msg) Copy() *Msg { return dns.CopyTo(new(Msg)) } @@ -1084,8 +1119,8 @@ func (dns *Msg) CopyTo(r1 *Msg) *Msg { return r1 } -func (q *Question) pack(msg []byte, off int, compression map[string]int, compress bool) (int, error) { - off, err := PackDomainName(q.Name, msg, off, compression, compress) +func (q *Question) pack(msg []byte, off int, compression compressionMap, compress bool) (int, error) { + off, _, err := packDomainName(q.Name, msg, off, compression, compress) if err != nil { return off, err } @@ -1126,7 +1161,7 @@ func unpackQuestion(msg []byte, off int) (Question, int, error) { return q, off, err } -func (dh *Header) pack(msg []byte, off int, compression map[string]int, compress bool) (int, error) { +func (dh *Header) pack(msg []byte, off int, compression compressionMap, compress bool) (int, error) { off, err := packUint16(dh.Id, msg, off) if err != nil { return off, err @@ -1179,3 +1214,18 @@ func unpackMsgHdr(msg []byte, off int) (Header, int, error) { dh.Arcount, off, err = unpackUint16(msg, off) return dh, off, err } + +// setHdr set the header in the dns using the binary data in dh. +func (dns *Msg) setHdr(dh Header) { + dns.Id = dh.Id + dns.Response = dh.Bits&_QR != 0 + dns.Opcode = int(dh.Bits>>11) & 0xF + dns.Authoritative = dh.Bits&_AA != 0 + dns.Truncated = dh.Bits&_TC != 0 + dns.RecursionDesired = dh.Bits&_RD != 0 + dns.RecursionAvailable = dh.Bits&_RA != 0 + dns.Zero = dh.Bits&_Z != 0 // _Z covers the zero bit, which should be zero; not sure why we set it to the opposite. + dns.AuthenticatedData = dh.Bits&_AD != 0 + dns.CheckingDisabled = dh.Bits&_CD != 0 + dns.Rcode = int(dh.Bits & 0xF) +} diff --git a/vendor/github.com/miekg/dns/msg_generate.go b/vendor/github.com/miekg/dns/msg_generate.go index 8ba609f72..86ed04fcb 100644 --- a/vendor/github.com/miekg/dns/msg_generate.go +++ b/vendor/github.com/miekg/dns/msg_generate.go @@ -80,18 +80,17 @@ func main() { o := scope.Lookup(name) st, _ := getTypeStruct(o.Type(), scope) - fmt.Fprintf(b, "func (rr *%s) pack(msg []byte, off int, compression map[string]int, compress bool) (int, error) {\n", name) - fmt.Fprint(b, `off, err := rr.Hdr.pack(msg, off, compression, compress) + fmt.Fprintf(b, "func (rr *%s) pack(msg []byte, off int, compression compressionMap, compress bool) (int, int, error) {\n", name) + fmt.Fprint(b, `headerEnd, off, err := rr.Hdr.pack(msg, off, compression, compress) if err != nil { - return off, err + return headerEnd, off, err } -headerEnd := off `) for i := 1; i < st.NumFields(); i++ { o := func(s string) { fmt.Fprintf(b, s, st.Field(i).Name()) fmt.Fprint(b, `if err != nil { -return off, err +return headerEnd, off, err } `) } @@ -106,7 +105,7 @@ return off, err case `dns:"nsec"`: o("off, err = packDataNsec(rr.%s, msg, off)\n") case `dns:"domain-name"`: - o("off, err = packDataDomainNames(rr.%s, msg, off, compression, compress)\n") + o("off, err = packDataDomainNames(rr.%s, msg, off, compression, false)\n") default: log.Fatalln(name, st.Field(i).Name(), st.Tag(i)) } @@ -116,9 +115,9 @@ return off, err switch { case st.Tag(i) == `dns:"-"`: // ignored case st.Tag(i) == `dns:"cdomain-name"`: - o("off, err = PackDomainName(rr.%s, msg, off, compression, compress)\n") + o("off, _, err = packDomainName(rr.%s, msg, off, compression, compress)\n") case st.Tag(i) == `dns:"domain-name"`: - o("off, err = PackDomainName(rr.%s, msg, off, compression, false)\n") + o("off, _, err = packDomainName(rr.%s, msg, off, compression, false)\n") case st.Tag(i) == `dns:"a"`: o("off, err = packDataA(rr.%s, msg, off)\n") case st.Tag(i) == `dns:"aaaa"`: @@ -145,7 +144,7 @@ return off, err if rr.%s != "-" { off, err = packStringHex(rr.%s, msg, off) if err != nil { - return off, err + return headerEnd, off, err } } `, field, field) @@ -176,9 +175,7 @@ if rr.%s != "-" { log.Fatalln(name, st.Field(i).Name(), st.Tag(i)) } } - // We have packed everything, only now we know the rdlength of this RR - fmt.Fprintln(b, "rr.Header().Rdlength = uint16(off-headerEnd)") - fmt.Fprintln(b, "return off, nil }\n") + fmt.Fprintln(b, "return headerEnd, off, nil }\n") } fmt.Fprint(b, "// unpack*() functions\n\n") diff --git a/vendor/github.com/miekg/dns/msg_helpers.go b/vendor/github.com/miekg/dns/msg_helpers.go index 81fc2b1be..36345e162 100644 --- a/vendor/github.com/miekg/dns/msg_helpers.go +++ b/vendor/github.com/miekg/dns/msg_helpers.go @@ -101,32 +101,32 @@ func unpackHeader(msg []byte, off int) (rr RR_Header, off1 int, truncmsg []byte, // pack packs an RR header, returning the offset to the end of the header. // See PackDomainName for documentation about the compression. -func (hdr RR_Header) pack(msg []byte, off int, compression map[string]int, compress bool) (off1 int, err error) { +func (hdr RR_Header) pack(msg []byte, off int, compression compressionMap, compress bool) (int, int, error) { if off == len(msg) { - return off, nil + return off, off, nil } - off, err = PackDomainName(hdr.Name, msg, off, compression, compress) + off, _, err := packDomainName(hdr.Name, msg, off, compression, compress) if err != nil { - return len(msg), err + return off, len(msg), err } off, err = packUint16(hdr.Rrtype, msg, off) if err != nil { - return len(msg), err + return off, len(msg), err } off, err = packUint16(hdr.Class, msg, off) if err != nil { - return len(msg), err + return off, len(msg), err } off, err = packUint32(hdr.Ttl, msg, off) if err != nil { - return len(msg), err + return off, len(msg), err } - off, err = packUint16(hdr.Rdlength, msg, off) + off, err = packUint16(0, msg, off) // The RDLENGTH field will be set later in packRR. if err != nil { - return len(msg), err + return off, len(msg), err } - return off, nil + return off, off, nil } // helper helper functions. @@ -223,8 +223,8 @@ func unpackUint48(msg []byte, off int) (i uint64, off1 int, err error) { return 0, len(msg), &Error{err: "overflow unpacking uint64 as uint48"} } // Used in TSIG where the last 48 bits are occupied, so for now, assume a uint48 (6 bytes) - i = uint64(uint64(msg[off])<<40 | uint64(msg[off+1])<<32 | uint64(msg[off+2])<<24 | uint64(msg[off+3])<<16 | - uint64(msg[off+4])<<8 | uint64(msg[off+5])) + i = uint64(msg[off])<<40 | uint64(msg[off+1])<<32 | uint64(msg[off+2])<<24 | uint64(msg[off+3])<<16 | + uint64(msg[off+4])<<8 | uint64(msg[off+5]) off += 6 return i, off, nil } @@ -275,7 +275,7 @@ func unpackString(msg []byte, off int) (string, int, error) { s.WriteByte('\\') s.WriteByte(b) case b < ' ' || b > '~': // unprintable - writeEscapedByte(&s, b) + s.WriteString(escapeByte(b)) default: s.WriteByte(b) } @@ -621,10 +621,10 @@ func unpackDataDomainNames(msg []byte, off, end int) ([]string, int, error) { return servers, off, nil } -func packDataDomainNames(names []string, msg []byte, off int, compression map[string]int, compress bool) (int, error) { +func packDataDomainNames(names []string, msg []byte, off int, compression compressionMap, compress bool) (int, error) { var err error for j := 0; j < len(names); j++ { - off, err = PackDomainName(names[j], msg, off, compression, false && compress) + off, _, err = packDomainName(names[j], msg, off, compression, compress) if err != nil { return len(msg), err } diff --git a/vendor/github.com/miekg/dns/nsecx.go b/vendor/github.com/miekg/dns/nsecx.go index 9b908c447..8f071a473 100644 --- a/vendor/github.com/miekg/dns/nsecx.go +++ b/vendor/github.com/miekg/dns/nsecx.go @@ -2,49 +2,44 @@ package dns import ( "crypto/sha1" - "hash" + "encoding/hex" "strings" ) -type saltWireFmt struct { - Salt string `dns:"size-hex"` -} - // HashName hashes a string (label) according to RFC 5155. It returns the hashed string in uppercase. func HashName(label string, ha uint8, iter uint16, salt string) string { - saltwire := new(saltWireFmt) - saltwire.Salt = salt - wire := make([]byte, DefaultMsgSize) - n, err := packSaltWire(saltwire, wire) + if ha != SHA1 { + return "" + } + + wireSalt := make([]byte, hex.DecodedLen(len(salt))) + n, err := packStringHex(salt, wireSalt, 0) if err != nil { return "" } - wire = wire[:n] + wireSalt = wireSalt[:n] + name := make([]byte, 255) off, err := PackDomainName(strings.ToLower(label), name, 0, nil, false) if err != nil { return "" } name = name[:off] - var s hash.Hash - switch ha { - case SHA1: - s = sha1.New() - default: - return "" - } + s := sha1.New() // k = 0 s.Write(name) - s.Write(wire) + s.Write(wireSalt) nsec3 := s.Sum(nil) + // k > 0 for k := uint16(0); k < iter; k++ { s.Reset() s.Write(nsec3) - s.Write(wire) + s.Write(wireSalt) nsec3 = s.Sum(nsec3[:0]) } + return toBase32(nsec3) } @@ -63,8 +58,10 @@ func (rr *NSEC3) Cover(name string) bool { } nextHash := rr.NextDomain - if ownerHash == nextHash { // empty interval - return false + + // if empty interval found, try cover wildcard hashes so nameHash shouldn't match with ownerHash + if ownerHash == nextHash && nameHash != ownerHash { // empty interval + return true } if ownerHash > nextHash { // end of zone if nameHash > ownerHash { // covered since there is nothing after ownerHash @@ -96,11 +93,3 @@ func (rr *NSEC3) Match(name string) bool { } return false } - -func packSaltWire(sw *saltWireFmt, msg []byte) (int, error) { - off, err := packStringHex(sw.Salt, msg, 0) - if err != nil { - return off, err - } - return off, nil -} diff --git a/vendor/github.com/miekg/dns/privaterr.go b/vendor/github.com/miekg/dns/privaterr.go index 74544a74e..18355f9be 100644 --- a/vendor/github.com/miekg/dns/privaterr.go +++ b/vendor/github.com/miekg/dns/privaterr.go @@ -52,7 +52,12 @@ func (r *PrivateRR) Header() *RR_Header { return &r.Hdr } func (r *PrivateRR) String() string { return r.Hdr.String() + r.Data.String() } // Private len and copy parts to satisfy RR interface. -func (r *PrivateRR) len() int { return r.Hdr.len() + r.Data.Len() } +func (r *PrivateRR) len(off int, compression map[string]struct{}) int { + l := r.Hdr.len(off, compression) + l += r.Data.Len() + return l +} + func (r *PrivateRR) copy() RR { // make new RR like this: rr := mkPrivateRR(r.Hdr.Rrtype) @@ -64,19 +69,18 @@ func (r *PrivateRR) copy() RR { } return rr } -func (r *PrivateRR) pack(msg []byte, off int, compression map[string]int, compress bool) (int, error) { - off, err := r.Hdr.pack(msg, off, compression, compress) + +func (r *PrivateRR) pack(msg []byte, off int, compression compressionMap, compress bool) (int, int, error) { + headerEnd, off, err := r.Hdr.pack(msg, off, compression, compress) if err != nil { - return off, err + return off, off, err } - headerEnd := off n, err := r.Data.Pack(msg[off:]) if err != nil { - return len(msg), err + return headerEnd, len(msg), err } off += n - r.Header().Rdlength = uint16(off - headerEnd) - return off, nil + return headerEnd, off, nil } // PrivateHandle registers a private resource record type. It requires diff --git a/vendor/github.com/miekg/dns/rawmsg.go b/vendor/github.com/miekg/dns/rawmsg.go deleted file mode 100644 index 6e21fba7e..000000000 --- a/vendor/github.com/miekg/dns/rawmsg.go +++ /dev/null @@ -1,49 +0,0 @@ -package dns - -import "encoding/binary" - -// rawSetRdlength sets the rdlength in the header of -// the RR. The offset 'off' must be positioned at the -// start of the header of the RR, 'end' must be the -// end of the RR. -func rawSetRdlength(msg []byte, off, end int) bool { - l := len(msg) -Loop: - for { - if off+1 > l { - return false - } - c := int(msg[off]) - off++ - switch c & 0xC0 { - case 0x00: - if c == 0x00 { - // End of the domainname - break Loop - } - if off+c > l { - return false - } - off += c - - case 0xC0: - // pointer, next byte included, ends domainname - off++ - break Loop - } - } - // The domainname has been seen, we at the start of the fixed part in the header. - // Type is 2 bytes, class is 2 bytes, ttl 4 and then 2 bytes for the length. - off += 2 + 2 + 4 - if off+2 > l { - return false - } - //off+1 is the end of the header, 'end' is the end of the rr - //so 'end' - 'off+2' is the length of the rdata - rdatalen := end - (off + 2) - if rdatalen > 0xFFFF { - return false - } - binary.BigEndian.PutUint16(msg[off:], uint16(rdatalen)) - return true -} diff --git a/vendor/github.com/miekg/dns/reverse.go b/vendor/github.com/miekg/dns/reverse.go index f6e7a47a6..1f0e2b2a4 100644 --- a/vendor/github.com/miekg/dns/reverse.go +++ b/vendor/github.com/miekg/dns/reverse.go @@ -12,6 +12,11 @@ var StringToOpcode = reverseInt(OpcodeToString) // StringToRcode is a map of rcodes to strings. var StringToRcode = reverseInt(RcodeToString) +func init() { + // Preserve previous NOTIMP typo, see github.com/miekg/dns/issues/733. + StringToRcode["NOTIMPL"] = RcodeNotImplemented +} + // Reverse a map func reverseInt8(m map[uint8]string) map[string]uint8 { n := make(map[string]uint8, len(m)) diff --git a/vendor/github.com/miekg/dns/server.go b/vendor/github.com/miekg/dns/server.go index 06984e7ca..6abbed512 100644 --- a/vendor/github.com/miekg/dns/server.go +++ b/vendor/github.com/miekg/dns/server.go @@ -203,9 +203,6 @@ type Server struct { IdleTimeout func() time.Duration // Secret(s) for Tsig map[]. The zonename must be in canonical form (lowercase, fqdn, see RFC 4034 Section 6.2). TsigSecret map[string]string - // Unsafe instructs the server to disregard any sanity checks and directly hand the message to - // the handler. It will specifically not check if the query has the QR bit not set. - Unsafe bool // If NotifyStartedFunc is set it is called once the server has started listening. NotifyStartedFunc func() // DecorateReader is optional, allows customization of the process that reads raw DNS messages. @@ -217,6 +214,9 @@ type Server struct { // Whether to set the SO_REUSEPORT socket option, allowing multiple listeners to be bound to a single address. // It is only supported on go1.11+ and when using ListenAndServe. ReusePort bool + // AcceptMsgFunc will check the incoming message and will reject it early in the process. + // By default DefaultMsgAcceptFunc will be used. + MsgAcceptFunc MsgAcceptFunc // UDP packet or TCP connection queue queue chan *response @@ -300,6 +300,9 @@ func (srv *Server) init() { if srv.UDPSize == 0 { srv.UDPSize = MinMsgSize } + if srv.MsgAcceptFunc == nil { + srv.MsgAcceptFunc = defaultMsgAcceptFunc + } srv.udpPool.New = makeUDPBuffer(srv.UDPSize) } @@ -630,14 +633,34 @@ func (srv *Server) disposeBuffer(w *response) { } func (srv *Server) serveDNS(w *response) { + dh, off, err := unpackMsgHdr(w.msg, 0) + if err != nil { + // Let client hang, they are sending crap; any reply can be used to amplify. + return + } + req := new(Msg) - err := req.Unpack(w.msg) - if err != nil { // Send a FormatError back - x := new(Msg) - x.SetRcodeFormatError(req) - w.WriteMsg(x) + req.setHdr(dh) + + switch srv.MsgAcceptFunc(dh) { + case MsgAccept: + case MsgIgnore: + return + case MsgReject: + req.SetRcodeFormatError(req) + // Are we allowed to delete any OPT records here? + req.Ns, req.Answer, req.Extra = nil, nil, nil + + w.WriteMsg(req) + srv.disposeBuffer(w) + return } - if err != nil || !srv.Unsafe && req.Response { + + if err := req.unpack(dh, w.msg, off); err != nil { + req.SetRcodeFormatError(req) + req.Ns, req.Answer, req.Extra = nil, nil, nil + + w.WriteMsg(req) srv.disposeBuffer(w) return } diff --git a/vendor/github.com/miekg/dns/sig0.go b/vendor/github.com/miekg/dns/sig0.go index 07c2acb19..e97f63968 100644 --- a/vendor/github.com/miekg/dns/sig0.go +++ b/vendor/github.com/miekg/dns/sig0.go @@ -29,7 +29,7 @@ func (rr *SIG) Sign(k crypto.Signer, m *Msg) ([]byte, error) { rr.TypeCovered = 0 rr.Labels = 0 - buf := make([]byte, m.Len()+rr.len()) + buf := make([]byte, m.Len()+Len(rr)) mbuf, err := m.PackBuffer(buf) if err != nil { return nil, err @@ -167,7 +167,7 @@ func (rr *SIG) Verify(k *KEY, buf []byte) error { } // If key has come from the DNS name compression might // have mangled the case of the name - if strings.ToLower(signername) != strings.ToLower(k.Header().Name) { + if !strings.EqualFold(signername, k.Header().Name) { return &Error{err: "signer name doesn't match key name"} } sigend := offset diff --git a/vendor/github.com/miekg/dns/tsig.go b/vendor/github.com/miekg/dns/tsig.go index 4837b4ab1..91b69d582 100644 --- a/vendor/github.com/miekg/dns/tsig.go +++ b/vendor/github.com/miekg/dns/tsig.go @@ -133,7 +133,7 @@ func TsigGenerate(m *Msg, secret, requestMAC string, timersOnly bool) ([]byte, s t.Algorithm = rr.Algorithm t.OrigId = m.Id - tbuf := make([]byte, t.len()) + tbuf := make([]byte, Len(t)) if off, err := PackRR(t, tbuf, 0, nil, false); err == nil { tbuf = tbuf[:off] // reset to actual size used } else { diff --git a/vendor/github.com/miekg/dns/types.go b/vendor/github.com/miekg/dns/types.go index 115f2c7bd..c8658b343 100644 --- a/vendor/github.com/miekg/dns/types.go +++ b/vendor/github.com/miekg/dns/types.go @@ -218,8 +218,10 @@ type Question struct { Qclass uint16 } -func (q *Question) len() int { - return len(q.Name) + 1 + 2 + 2 +func (q *Question) len(off int, compression map[string]struct{}) int { + l := domainNameLen(q.Name, off, compression, true) + l += 2 + 2 + return l } func (q *Question) String() (s string) { @@ -351,7 +353,7 @@ func (rr *X25) String() string { type RT struct { Hdr RR_Header Preference uint16 - Host string `dns:"cdomain-name"` + Host string `dns:"domain-name"` // RFC 3597 prohibits compressing records not defined in RFC 1035. } func (rr *RT) String() string { @@ -460,7 +462,7 @@ func sprintTxtOctet(s string) string { case b == '.': dst.WriteByte('.') case b < ' ' || b > '~': - writeEscapedByte(&dst, b) + dst.WriteString(escapeByte(b)) default: dst.WriteByte(b) } @@ -508,20 +510,44 @@ func writeTXTStringByte(s *strings.Builder, b byte) { s.WriteByte('\\') s.WriteByte(b) case b < ' ' || b > '~': - writeEscapedByte(s, b) + s.WriteString(escapeByte(b)) default: s.WriteByte(b) } } -func writeEscapedByte(s *strings.Builder, b byte) { - var buf [3]byte - bufs := strconv.AppendInt(buf[:0], int64(b), 10) - s.WriteByte('\\') - for i := len(bufs); i < 3; i++ { - s.WriteByte('0') +const ( + escapedByteSmall = "" + + `\000\001\002\003\004\005\006\007\008\009` + + `\010\011\012\013\014\015\016\017\018\019` + + `\020\021\022\023\024\025\026\027\028\029` + + `\030\031` + escapedByteLarge = `\127\128\129` + + `\130\131\132\133\134\135\136\137\138\139` + + `\140\141\142\143\144\145\146\147\148\149` + + `\150\151\152\153\154\155\156\157\158\159` + + `\160\161\162\163\164\165\166\167\168\169` + + `\170\171\172\173\174\175\176\177\178\179` + + `\180\181\182\183\184\185\186\187\188\189` + + `\190\191\192\193\194\195\196\197\198\199` + + `\200\201\202\203\204\205\206\207\208\209` + + `\210\211\212\213\214\215\216\217\218\219` + + `\220\221\222\223\224\225\226\227\228\229` + + `\230\231\232\233\234\235\236\237\238\239` + + `\240\241\242\243\244\245\246\247\248\249` + + `\250\251\252\253\254\255` +) + +// escapeByte returns the \DDD escaping of b which must +// satisfy b < ' ' || b > '~'. +func escapeByte(b byte) string { + if b < ' ' { + return escapedByteSmall[b*4 : b*4+4] } - s.Write(bufs) + + b -= '~' + 1 + // The cast here is needed as b*4 may overflow byte. + return escapedByteLarge[int(b)*4 : int(b)*4+4] } func nextByte(s string, offset int) (byte, int) { @@ -809,8 +835,9 @@ func (rr *NSEC) String() string { return s } -func (rr *NSEC) len() int { - l := rr.Hdr.len() + len(rr.NextDomain) + 1 +func (rr *NSEC) len(off int, compression map[string]struct{}) int { + l := rr.Hdr.len(off, compression) + l += domainNameLen(rr.NextDomain, off+l, compression, false) lastwindow := uint32(2 ^ 32 + 1) for _, t := range rr.TypeBitMap { window := t / 256 @@ -974,8 +1001,9 @@ func (rr *NSEC3) String() string { return s } -func (rr *NSEC3) len() int { - l := rr.Hdr.len() + 6 + len(rr.Salt)/2 + 1 + len(rr.NextDomain) + 1 +func (rr *NSEC3) len(off int, compression map[string]struct{}) int { + l := rr.Hdr.len(off, compression) + l += 6 + len(rr.Salt)/2 + 1 + len(rr.NextDomain) + 1 lastwindow := uint32(2 ^ 32 + 1) for _, t := range rr.TypeBitMap { window := t / 256 @@ -1291,8 +1319,9 @@ func (rr *CSYNC) String() string { return s } -func (rr *CSYNC) len() int { - l := rr.Hdr.len() + 4 + 2 +func (rr *CSYNC) len(off int, compression map[string]struct{}) int { + l := rr.Hdr.len(off, compression) + l += 4 + 2 lastwindow := uint32(2 ^ 32 + 1) for _, t := range rr.TypeBitMap { window := t / 256 diff --git a/vendor/github.com/miekg/dns/types_generate.go b/vendor/github.com/miekg/dns/types_generate.go index b8db4f361..8c897ec11 100644 --- a/vendor/github.com/miekg/dns/types_generate.go +++ b/vendor/github.com/miekg/dns/types_generate.go @@ -153,8 +153,8 @@ func main() { if isEmbedded { continue } - fmt.Fprintf(b, "func (rr *%s) len() int {\n", name) - fmt.Fprintf(b, "l := rr.Hdr.len()\n") + fmt.Fprintf(b, "func (rr *%s) len(off int, compression map[string]struct{}) int {\n", name) + fmt.Fprintf(b, "l := rr.Hdr.len(off, compression)\n") for i := 1; i < st.NumFields(); i++ { o := func(s string) { fmt.Fprintf(b, s, st.Field(i).Name()) } @@ -162,7 +162,11 @@ func main() { switch st.Tag(i) { case `dns:"-"`: // ignored - case `dns:"cdomain-name"`, `dns:"domain-name"`, `dns:"txt"`: + case `dns:"cdomain-name"`: + o("for _, x := range rr.%s { l += domainNameLen(x, off+l, compression, true) }\n") + case `dns:"domain-name"`: + o("for _, x := range rr.%s { l += domainNameLen(x, off+l, compression, false) }\n") + case `dns:"txt"`: o("for _, x := range rr.%s { l += len(x) + 1 }\n") default: log.Fatalln(name, st.Field(i).Name(), st.Tag(i)) @@ -173,8 +177,10 @@ func main() { switch { case st.Tag(i) == `dns:"-"`: // ignored - case st.Tag(i) == `dns:"cdomain-name"`, st.Tag(i) == `dns:"domain-name"`: - o("l += len(rr.%s) + 1\n") + case st.Tag(i) == `dns:"cdomain-name"`: + o("l += domainNameLen(rr.%s, off+l, compression, true)\n") + case st.Tag(i) == `dns:"domain-name"`: + o("l += domainNameLen(rr.%s, off+l, compression, false)\n") case st.Tag(i) == `dns:"octet"`: o("l += len(rr.%s)\n") case strings.HasPrefix(st.Tag(i), `dns:"size-base64`): diff --git a/vendor/github.com/miekg/dns/version.go b/vendor/github.com/miekg/dns/version.go index e7c82e999..257dee4d1 100644 --- a/vendor/github.com/miekg/dns/version.go +++ b/vendor/github.com/miekg/dns/version.go @@ -3,7 +3,7 @@ package dns import "fmt" // Version is current version of this library. -var Version = V{1, 0, 15} +var Version = V{1, 1, 1} // V holds the version of this library. type V struct { diff --git a/vendor/github.com/miekg/dns/zcompress.go b/vendor/github.com/miekg/dns/zcompress.go deleted file mode 100644 index 6391a3501..000000000 --- a/vendor/github.com/miekg/dns/zcompress.go +++ /dev/null @@ -1,152 +0,0 @@ -// Code generated by "go run compress_generate.go"; DO NOT EDIT. - -package dns - -func compressionLenHelperType(c map[string]int, r RR, initLen int) int { - currentLen := initLen - switch x := r.(type) { - case *AFSDB: - currentLen -= len(x.Hostname) + 1 - currentLen += compressionLenHelper(c, x.Hostname, currentLen) - case *CNAME: - currentLen -= len(x.Target) + 1 - currentLen += compressionLenHelper(c, x.Target, currentLen) - case *DNAME: - currentLen -= len(x.Target) + 1 - currentLen += compressionLenHelper(c, x.Target, currentLen) - case *HIP: - for i := range x.RendezvousServers { - currentLen -= len(x.RendezvousServers[i]) + 1 - } - for i := range x.RendezvousServers { - currentLen += compressionLenHelper(c, x.RendezvousServers[i], currentLen) - } - case *KX: - currentLen -= len(x.Exchanger) + 1 - currentLen += compressionLenHelper(c, x.Exchanger, currentLen) - case *LP: - currentLen -= len(x.Fqdn) + 1 - currentLen += compressionLenHelper(c, x.Fqdn, currentLen) - case *MB: - currentLen -= len(x.Mb) + 1 - currentLen += compressionLenHelper(c, x.Mb, currentLen) - case *MD: - currentLen -= len(x.Md) + 1 - currentLen += compressionLenHelper(c, x.Md, currentLen) - case *MF: - currentLen -= len(x.Mf) + 1 - currentLen += compressionLenHelper(c, x.Mf, currentLen) - case *MG: - currentLen -= len(x.Mg) + 1 - currentLen += compressionLenHelper(c, x.Mg, currentLen) - case *MINFO: - currentLen -= len(x.Rmail) + 1 - currentLen += compressionLenHelper(c, x.Rmail, currentLen) - currentLen -= len(x.Email) + 1 - currentLen += compressionLenHelper(c, x.Email, currentLen) - case *MR: - currentLen -= len(x.Mr) + 1 - currentLen += compressionLenHelper(c, x.Mr, currentLen) - case *MX: - currentLen -= len(x.Mx) + 1 - currentLen += compressionLenHelper(c, x.Mx, currentLen) - case *NAPTR: - currentLen -= len(x.Replacement) + 1 - currentLen += compressionLenHelper(c, x.Replacement, currentLen) - case *NS: - currentLen -= len(x.Ns) + 1 - currentLen += compressionLenHelper(c, x.Ns, currentLen) - case *NSAPPTR: - currentLen -= len(x.Ptr) + 1 - currentLen += compressionLenHelper(c, x.Ptr, currentLen) - case *NSEC: - currentLen -= len(x.NextDomain) + 1 - currentLen += compressionLenHelper(c, x.NextDomain, currentLen) - case *PTR: - currentLen -= len(x.Ptr) + 1 - currentLen += compressionLenHelper(c, x.Ptr, currentLen) - case *PX: - currentLen -= len(x.Map822) + 1 - currentLen += compressionLenHelper(c, x.Map822, currentLen) - currentLen -= len(x.Mapx400) + 1 - currentLen += compressionLenHelper(c, x.Mapx400, currentLen) - case *RP: - currentLen -= len(x.Mbox) + 1 - currentLen += compressionLenHelper(c, x.Mbox, currentLen) - currentLen -= len(x.Txt) + 1 - currentLen += compressionLenHelper(c, x.Txt, currentLen) - case *RRSIG: - currentLen -= len(x.SignerName) + 1 - currentLen += compressionLenHelper(c, x.SignerName, currentLen) - case *RT: - currentLen -= len(x.Host) + 1 - currentLen += compressionLenHelper(c, x.Host, currentLen) - case *SIG: - currentLen -= len(x.SignerName) + 1 - currentLen += compressionLenHelper(c, x.SignerName, currentLen) - case *SOA: - currentLen -= len(x.Ns) + 1 - currentLen += compressionLenHelper(c, x.Ns, currentLen) - currentLen -= len(x.Mbox) + 1 - currentLen += compressionLenHelper(c, x.Mbox, currentLen) - case *SRV: - currentLen -= len(x.Target) + 1 - currentLen += compressionLenHelper(c, x.Target, currentLen) - case *TALINK: - currentLen -= len(x.PreviousName) + 1 - currentLen += compressionLenHelper(c, x.PreviousName, currentLen) - currentLen -= len(x.NextName) + 1 - currentLen += compressionLenHelper(c, x.NextName, currentLen) - case *TKEY: - currentLen -= len(x.Algorithm) + 1 - currentLen += compressionLenHelper(c, x.Algorithm, currentLen) - case *TSIG: - currentLen -= len(x.Algorithm) + 1 - currentLen += compressionLenHelper(c, x.Algorithm, currentLen) - } - return currentLen - initLen -} - -func compressionLenSearchType(c map[string]int, r RR) (int, bool, int) { - switch x := r.(type) { - case *CNAME: - k1, ok1, sz1 := compressionLenSearch(c, x.Target) - return k1, ok1, sz1 - case *MB: - k1, ok1, sz1 := compressionLenSearch(c, x.Mb) - return k1, ok1, sz1 - case *MD: - k1, ok1, sz1 := compressionLenSearch(c, x.Md) - return k1, ok1, sz1 - case *MF: - k1, ok1, sz1 := compressionLenSearch(c, x.Mf) - return k1, ok1, sz1 - case *MG: - k1, ok1, sz1 := compressionLenSearch(c, x.Mg) - return k1, ok1, sz1 - case *MINFO: - k1, ok1, sz1 := compressionLenSearch(c, x.Rmail) - k2, ok2, sz2 := compressionLenSearch(c, x.Email) - return k1 + k2, ok1 && ok2, sz1 + sz2 - case *MR: - k1, ok1, sz1 := compressionLenSearch(c, x.Mr) - return k1, ok1, sz1 - case *MX: - k1, ok1, sz1 := compressionLenSearch(c, x.Mx) - return k1, ok1, sz1 - case *NS: - k1, ok1, sz1 := compressionLenSearch(c, x.Ns) - return k1, ok1, sz1 - case *PTR: - k1, ok1, sz1 := compressionLenSearch(c, x.Ptr) - return k1, ok1, sz1 - case *RT: - k1, ok1, sz1 := compressionLenSearch(c, x.Host) - return k1, ok1, sz1 - case *SOA: - k1, ok1, sz1 := compressionLenSearch(c, x.Ns) - k2, ok2, sz2 := compressionLenSearch(c, x.Mbox) - return k1 + k2, ok1 && ok2, sz1 + sz2 - } - return 0, false, 0 -} diff --git a/vendor/github.com/miekg/dns/zmsg.go b/vendor/github.com/miekg/dns/zmsg.go index 1a68f74da..3e93a782f 100644 --- a/vendor/github.com/miekg/dns/zmsg.go +++ b/vendor/github.com/miekg/dns/zmsg.go @@ -4,1490 +4,1350 @@ package dns // pack*() functions -func (rr *A) pack(msg []byte, off int, compression map[string]int, compress bool) (int, error) { - off, err := rr.Hdr.pack(msg, off, compression, compress) +func (rr *A) pack(msg []byte, off int, compression compressionMap, compress bool) (int, int, error) { + headerEnd, off, err := rr.Hdr.pack(msg, off, compression, compress) if err != nil { - return off, err + return headerEnd, off, err } - headerEnd := off off, err = packDataA(rr.A, msg, off) if err != nil { - return off, err + return headerEnd, off, err } - rr.Header().Rdlength = uint16(off - headerEnd) - return off, nil + return headerEnd, off, nil } -func (rr *AAAA) pack(msg []byte, off int, compression map[string]int, compress bool) (int, error) { - off, err := rr.Hdr.pack(msg, off, compression, compress) +func (rr *AAAA) pack(msg []byte, off int, compression compressionMap, compress bool) (int, int, error) { + headerEnd, off, err := rr.Hdr.pack(msg, off, compression, compress) if err != nil { - return off, err + return headerEnd, off, err } - headerEnd := off off, err = packDataAAAA(rr.AAAA, msg, off) if err != nil { - return off, err + return headerEnd, off, err } - rr.Header().Rdlength = uint16(off - headerEnd) - return off, nil + return headerEnd, off, nil } -func (rr *AFSDB) pack(msg []byte, off int, compression map[string]int, compress bool) (int, error) { - off, err := rr.Hdr.pack(msg, off, compression, compress) +func (rr *AFSDB) pack(msg []byte, off int, compression compressionMap, compress bool) (int, int, error) { + headerEnd, off, err := rr.Hdr.pack(msg, off, compression, compress) if err != nil { - return off, err + return headerEnd, off, err } - headerEnd := off off, err = packUint16(rr.Subtype, msg, off) if err != nil { - return off, err + return headerEnd, off, err } - off, err = PackDomainName(rr.Hostname, msg, off, compression, false) + off, _, err = packDomainName(rr.Hostname, msg, off, compression, false) if err != nil { - return off, err + return headerEnd, off, err } - rr.Header().Rdlength = uint16(off - headerEnd) - return off, nil + return headerEnd, off, nil } -func (rr *ANY) pack(msg []byte, off int, compression map[string]int, compress bool) (int, error) { - off, err := rr.Hdr.pack(msg, off, compression, compress) +func (rr *ANY) pack(msg []byte, off int, compression compressionMap, compress bool) (int, int, error) { + headerEnd, off, err := rr.Hdr.pack(msg, off, compression, compress) if err != nil { - return off, err + return headerEnd, off, err } - headerEnd := off - rr.Header().Rdlength = uint16(off - headerEnd) - return off, nil + return headerEnd, off, nil } -func (rr *AVC) pack(msg []byte, off int, compression map[string]int, compress bool) (int, error) { - off, err := rr.Hdr.pack(msg, off, compression, compress) +func (rr *AVC) pack(msg []byte, off int, compression compressionMap, compress bool) (int, int, error) { + headerEnd, off, err := rr.Hdr.pack(msg, off, compression, compress) if err != nil { - return off, err + return headerEnd, off, err } - headerEnd := off off, err = packStringTxt(rr.Txt, msg, off) if err != nil { - return off, err + return headerEnd, off, err } - rr.Header().Rdlength = uint16(off - headerEnd) - return off, nil + return headerEnd, off, nil } -func (rr *CAA) pack(msg []byte, off int, compression map[string]int, compress bool) (int, error) { - off, err := rr.Hdr.pack(msg, off, compression, compress) +func (rr *CAA) pack(msg []byte, off int, compression compressionMap, compress bool) (int, int, error) { + headerEnd, off, err := rr.Hdr.pack(msg, off, compression, compress) if err != nil { - return off, err + return headerEnd, off, err } - headerEnd := off off, err = packUint8(rr.Flag, msg, off) if err != nil { - return off, err + return headerEnd, off, err } off, err = packString(rr.Tag, msg, off) if err != nil { - return off, err + return headerEnd, off, err } off, err = packStringOctet(rr.Value, msg, off) if err != nil { - return off, err + return headerEnd, off, err } - rr.Header().Rdlength = uint16(off - headerEnd) - return off, nil + return headerEnd, off, nil } -func (rr *CDNSKEY) pack(msg []byte, off int, compression map[string]int, compress bool) (int, error) { - off, err := rr.Hdr.pack(msg, off, compression, compress) +func (rr *CDNSKEY) pack(msg []byte, off int, compression compressionMap, compress bool) (int, int, error) { + headerEnd, off, err := rr.Hdr.pack(msg, off, compression, compress) if err != nil { - return off, err + return headerEnd, off, err } - headerEnd := off off, err = packUint16(rr.Flags, msg, off) if err != nil { - return off, err + return headerEnd, off, err } off, err = packUint8(rr.Protocol, msg, off) if err != nil { - return off, err + return headerEnd, off, err } off, err = packUint8(rr.Algorithm, msg, off) if err != nil { - return off, err + return headerEnd, off, err } off, err = packStringBase64(rr.PublicKey, msg, off) if err != nil { - return off, err + return headerEnd, off, err } - rr.Header().Rdlength = uint16(off - headerEnd) - return off, nil + return headerEnd, off, nil } -func (rr *CDS) pack(msg []byte, off int, compression map[string]int, compress bool) (int, error) { - off, err := rr.Hdr.pack(msg, off, compression, compress) +func (rr *CDS) pack(msg []byte, off int, compression compressionMap, compress bool) (int, int, error) { + headerEnd, off, err := rr.Hdr.pack(msg, off, compression, compress) if err != nil { - return off, err + return headerEnd, off, err } - headerEnd := off off, err = packUint16(rr.KeyTag, msg, off) if err != nil { - return off, err + return headerEnd, off, err } off, err = packUint8(rr.Algorithm, msg, off) if err != nil { - return off, err + return headerEnd, off, err } off, err = packUint8(rr.DigestType, msg, off) if err != nil { - return off, err + return headerEnd, off, err } off, err = packStringHex(rr.Digest, msg, off) if err != nil { - return off, err + return headerEnd, off, err } - rr.Header().Rdlength = uint16(off - headerEnd) - return off, nil + return headerEnd, off, nil } -func (rr *CERT) pack(msg []byte, off int, compression map[string]int, compress bool) (int, error) { - off, err := rr.Hdr.pack(msg, off, compression, compress) +func (rr *CERT) pack(msg []byte, off int, compression compressionMap, compress bool) (int, int, error) { + headerEnd, off, err := rr.Hdr.pack(msg, off, compression, compress) if err != nil { - return off, err + return headerEnd, off, err } - headerEnd := off off, err = packUint16(rr.Type, msg, off) if err != nil { - return off, err + return headerEnd, off, err } off, err = packUint16(rr.KeyTag, msg, off) if err != nil { - return off, err + return headerEnd, off, err } off, err = packUint8(rr.Algorithm, msg, off) if err != nil { - return off, err + return headerEnd, off, err } off, err = packStringBase64(rr.Certificate, msg, off) if err != nil { - return off, err + return headerEnd, off, err } - rr.Header().Rdlength = uint16(off - headerEnd) - return off, nil + return headerEnd, off, nil } -func (rr *CNAME) pack(msg []byte, off int, compression map[string]int, compress bool) (int, error) { - off, err := rr.Hdr.pack(msg, off, compression, compress) +func (rr *CNAME) pack(msg []byte, off int, compression compressionMap, compress bool) (int, int, error) { + headerEnd, off, err := rr.Hdr.pack(msg, off, compression, compress) if err != nil { - return off, err + return headerEnd, off, err } - headerEnd := off - off, err = PackDomainName(rr.Target, msg, off, compression, compress) + off, _, err = packDomainName(rr.Target, msg, off, compression, compress) if err != nil { - return off, err + return headerEnd, off, err } - rr.Header().Rdlength = uint16(off - headerEnd) - return off, nil + return headerEnd, off, nil } -func (rr *CSYNC) pack(msg []byte, off int, compression map[string]int, compress bool) (int, error) { - off, err := rr.Hdr.pack(msg, off, compression, compress) +func (rr *CSYNC) pack(msg []byte, off int, compression compressionMap, compress bool) (int, int, error) { + headerEnd, off, err := rr.Hdr.pack(msg, off, compression, compress) if err != nil { - return off, err + return headerEnd, off, err } - headerEnd := off off, err = packUint32(rr.Serial, msg, off) if err != nil { - return off, err + return headerEnd, off, err } off, err = packUint16(rr.Flags, msg, off) if err != nil { - return off, err + return headerEnd, off, err } off, err = packDataNsec(rr.TypeBitMap, msg, off) if err != nil { - return off, err + return headerEnd, off, err } - rr.Header().Rdlength = uint16(off - headerEnd) - return off, nil + return headerEnd, off, nil } -func (rr *DHCID) pack(msg []byte, off int, compression map[string]int, compress bool) (int, error) { - off, err := rr.Hdr.pack(msg, off, compression, compress) +func (rr *DHCID) pack(msg []byte, off int, compression compressionMap, compress bool) (int, int, error) { + headerEnd, off, err := rr.Hdr.pack(msg, off, compression, compress) if err != nil { - return off, err + return headerEnd, off, err } - headerEnd := off off, err = packStringBase64(rr.Digest, msg, off) if err != nil { - return off, err + return headerEnd, off, err } - rr.Header().Rdlength = uint16(off - headerEnd) - return off, nil + return headerEnd, off, nil } -func (rr *DLV) pack(msg []byte, off int, compression map[string]int, compress bool) (int, error) { - off, err := rr.Hdr.pack(msg, off, compression, compress) +func (rr *DLV) pack(msg []byte, off int, compression compressionMap, compress bool) (int, int, error) { + headerEnd, off, err := rr.Hdr.pack(msg, off, compression, compress) if err != nil { - return off, err + return headerEnd, off, err } - headerEnd := off off, err = packUint16(rr.KeyTag, msg, off) if err != nil { - return off, err + return headerEnd, off, err } off, err = packUint8(rr.Algorithm, msg, off) if err != nil { - return off, err + return headerEnd, off, err } off, err = packUint8(rr.DigestType, msg, off) if err != nil { - return off, err + return headerEnd, off, err } off, err = packStringHex(rr.Digest, msg, off) if err != nil { - return off, err + return headerEnd, off, err } - rr.Header().Rdlength = uint16(off - headerEnd) - return off, nil + return headerEnd, off, nil } -func (rr *DNAME) pack(msg []byte, off int, compression map[string]int, compress bool) (int, error) { - off, err := rr.Hdr.pack(msg, off, compression, compress) +func (rr *DNAME) pack(msg []byte, off int, compression compressionMap, compress bool) (int, int, error) { + headerEnd, off, err := rr.Hdr.pack(msg, off, compression, compress) if err != nil { - return off, err + return headerEnd, off, err } - headerEnd := off - off, err = PackDomainName(rr.Target, msg, off, compression, false) + off, _, err = packDomainName(rr.Target, msg, off, compression, false) if err != nil { - return off, err + return headerEnd, off, err } - rr.Header().Rdlength = uint16(off - headerEnd) - return off, nil + return headerEnd, off, nil } -func (rr *DNSKEY) pack(msg []byte, off int, compression map[string]int, compress bool) (int, error) { - off, err := rr.Hdr.pack(msg, off, compression, compress) +func (rr *DNSKEY) pack(msg []byte, off int, compression compressionMap, compress bool) (int, int, error) { + headerEnd, off, err := rr.Hdr.pack(msg, off, compression, compress) if err != nil { - return off, err + return headerEnd, off, err } - headerEnd := off off, err = packUint16(rr.Flags, msg, off) if err != nil { - return off, err + return headerEnd, off, err } off, err = packUint8(rr.Protocol, msg, off) if err != nil { - return off, err + return headerEnd, off, err } off, err = packUint8(rr.Algorithm, msg, off) if err != nil { - return off, err + return headerEnd, off, err } off, err = packStringBase64(rr.PublicKey, msg, off) if err != nil { - return off, err + return headerEnd, off, err } - rr.Header().Rdlength = uint16(off - headerEnd) - return off, nil + return headerEnd, off, nil } -func (rr *DS) pack(msg []byte, off int, compression map[string]int, compress bool) (int, error) { - off, err := rr.Hdr.pack(msg, off, compression, compress) +func (rr *DS) pack(msg []byte, off int, compression compressionMap, compress bool) (int, int, error) { + headerEnd, off, err := rr.Hdr.pack(msg, off, compression, compress) if err != nil { - return off, err + return headerEnd, off, err } - headerEnd := off off, err = packUint16(rr.KeyTag, msg, off) if err != nil { - return off, err + return headerEnd, off, err } off, err = packUint8(rr.Algorithm, msg, off) if err != nil { - return off, err + return headerEnd, off, err } off, err = packUint8(rr.DigestType, msg, off) if err != nil { - return off, err + return headerEnd, off, err } off, err = packStringHex(rr.Digest, msg, off) if err != nil { - return off, err + return headerEnd, off, err } - rr.Header().Rdlength = uint16(off - headerEnd) - return off, nil + return headerEnd, off, nil } -func (rr *EID) pack(msg []byte, off int, compression map[string]int, compress bool) (int, error) { - off, err := rr.Hdr.pack(msg, off, compression, compress) +func (rr *EID) pack(msg []byte, off int, compression compressionMap, compress bool) (int, int, error) { + headerEnd, off, err := rr.Hdr.pack(msg, off, compression, compress) if err != nil { - return off, err + return headerEnd, off, err } - headerEnd := off off, err = packStringHex(rr.Endpoint, msg, off) if err != nil { - return off, err + return headerEnd, off, err } - rr.Header().Rdlength = uint16(off - headerEnd) - return off, nil + return headerEnd, off, nil } -func (rr *EUI48) pack(msg []byte, off int, compression map[string]int, compress bool) (int, error) { - off, err := rr.Hdr.pack(msg, off, compression, compress) +func (rr *EUI48) pack(msg []byte, off int, compression compressionMap, compress bool) (int, int, error) { + headerEnd, off, err := rr.Hdr.pack(msg, off, compression, compress) if err != nil { - return off, err + return headerEnd, off, err } - headerEnd := off off, err = packUint48(rr.Address, msg, off) if err != nil { - return off, err + return headerEnd, off, err } - rr.Header().Rdlength = uint16(off - headerEnd) - return off, nil + return headerEnd, off, nil } -func (rr *EUI64) pack(msg []byte, off int, compression map[string]int, compress bool) (int, error) { - off, err := rr.Hdr.pack(msg, off, compression, compress) +func (rr *EUI64) pack(msg []byte, off int, compression compressionMap, compress bool) (int, int, error) { + headerEnd, off, err := rr.Hdr.pack(msg, off, compression, compress) if err != nil { - return off, err + return headerEnd, off, err } - headerEnd := off off, err = packUint64(rr.Address, msg, off) if err != nil { - return off, err + return headerEnd, off, err } - rr.Header().Rdlength = uint16(off - headerEnd) - return off, nil + return headerEnd, off, nil } -func (rr *GID) pack(msg []byte, off int, compression map[string]int, compress bool) (int, error) { - off, err := rr.Hdr.pack(msg, off, compression, compress) +func (rr *GID) pack(msg []byte, off int, compression compressionMap, compress bool) (int, int, error) { + headerEnd, off, err := rr.Hdr.pack(msg, off, compression, compress) if err != nil { - return off, err + return headerEnd, off, err } - headerEnd := off off, err = packUint32(rr.Gid, msg, off) if err != nil { - return off, err + return headerEnd, off, err } - rr.Header().Rdlength = uint16(off - headerEnd) - return off, nil + return headerEnd, off, nil } -func (rr *GPOS) pack(msg []byte, off int, compression map[string]int, compress bool) (int, error) { - off, err := rr.Hdr.pack(msg, off, compression, compress) +func (rr *GPOS) pack(msg []byte, off int, compression compressionMap, compress bool) (int, int, error) { + headerEnd, off, err := rr.Hdr.pack(msg, off, compression, compress) if err != nil { - return off, err + return headerEnd, off, err } - headerEnd := off off, err = packString(rr.Longitude, msg, off) if err != nil { - return off, err + return headerEnd, off, err } off, err = packString(rr.Latitude, msg, off) if err != nil { - return off, err + return headerEnd, off, err } off, err = packString(rr.Altitude, msg, off) if err != nil { - return off, err + return headerEnd, off, err } - rr.Header().Rdlength = uint16(off - headerEnd) - return off, nil + return headerEnd, off, nil } -func (rr *HINFO) pack(msg []byte, off int, compression map[string]int, compress bool) (int, error) { - off, err := rr.Hdr.pack(msg, off, compression, compress) +func (rr *HINFO) pack(msg []byte, off int, compression compressionMap, compress bool) (int, int, error) { + headerEnd, off, err := rr.Hdr.pack(msg, off, compression, compress) if err != nil { - return off, err + return headerEnd, off, err } - headerEnd := off off, err = packString(rr.Cpu, msg, off) if err != nil { - return off, err + return headerEnd, off, err } off, err = packString(rr.Os, msg, off) if err != nil { - return off, err + return headerEnd, off, err } - rr.Header().Rdlength = uint16(off - headerEnd) - return off, nil + return headerEnd, off, nil } -func (rr *HIP) pack(msg []byte, off int, compression map[string]int, compress bool) (int, error) { - off, err := rr.Hdr.pack(msg, off, compression, compress) +func (rr *HIP) pack(msg []byte, off int, compression compressionMap, compress bool) (int, int, error) { + headerEnd, off, err := rr.Hdr.pack(msg, off, compression, compress) if err != nil { - return off, err + return headerEnd, off, err } - headerEnd := off off, err = packUint8(rr.HitLength, msg, off) if err != nil { - return off, err + return headerEnd, off, err } off, err = packUint8(rr.PublicKeyAlgorithm, msg, off) if err != nil { - return off, err + return headerEnd, off, err } off, err = packUint16(rr.PublicKeyLength, msg, off) if err != nil { - return off, err + return headerEnd, off, err } off, err = packStringHex(rr.Hit, msg, off) if err != nil { - return off, err + return headerEnd, off, err } off, err = packStringBase64(rr.PublicKey, msg, off) if err != nil { - return off, err + return headerEnd, off, err } - off, err = packDataDomainNames(rr.RendezvousServers, msg, off, compression, compress) + off, err = packDataDomainNames(rr.RendezvousServers, msg, off, compression, false) if err != nil { - return off, err + return headerEnd, off, err } - rr.Header().Rdlength = uint16(off - headerEnd) - return off, nil + return headerEnd, off, nil } -func (rr *KEY) pack(msg []byte, off int, compression map[string]int, compress bool) (int, error) { - off, err := rr.Hdr.pack(msg, off, compression, compress) +func (rr *KEY) pack(msg []byte, off int, compression compressionMap, compress bool) (int, int, error) { + headerEnd, off, err := rr.Hdr.pack(msg, off, compression, compress) if err != nil { - return off, err + return headerEnd, off, err } - headerEnd := off off, err = packUint16(rr.Flags, msg, off) if err != nil { - return off, err + return headerEnd, off, err } off, err = packUint8(rr.Protocol, msg, off) if err != nil { - return off, err + return headerEnd, off, err } off, err = packUint8(rr.Algorithm, msg, off) if err != nil { - return off, err + return headerEnd, off, err } off, err = packStringBase64(rr.PublicKey, msg, off) if err != nil { - return off, err + return headerEnd, off, err } - rr.Header().Rdlength = uint16(off - headerEnd) - return off, nil + return headerEnd, off, nil } -func (rr *KX) pack(msg []byte, off int, compression map[string]int, compress bool) (int, error) { - off, err := rr.Hdr.pack(msg, off, compression, compress) +func (rr *KX) pack(msg []byte, off int, compression compressionMap, compress bool) (int, int, error) { + headerEnd, off, err := rr.Hdr.pack(msg, off, compression, compress) if err != nil { - return off, err + return headerEnd, off, err } - headerEnd := off off, err = packUint16(rr.Preference, msg, off) if err != nil { - return off, err + return headerEnd, off, err } - off, err = PackDomainName(rr.Exchanger, msg, off, compression, false) + off, _, err = packDomainName(rr.Exchanger, msg, off, compression, false) if err != nil { - return off, err + return headerEnd, off, err } - rr.Header().Rdlength = uint16(off - headerEnd) - return off, nil + return headerEnd, off, nil } -func (rr *L32) pack(msg []byte, off int, compression map[string]int, compress bool) (int, error) { - off, err := rr.Hdr.pack(msg, off, compression, compress) +func (rr *L32) pack(msg []byte, off int, compression compressionMap, compress bool) (int, int, error) { + headerEnd, off, err := rr.Hdr.pack(msg, off, compression, compress) if err != nil { - return off, err + return headerEnd, off, err } - headerEnd := off off, err = packUint16(rr.Preference, msg, off) if err != nil { - return off, err + return headerEnd, off, err } off, err = packDataA(rr.Locator32, msg, off) if err != nil { - return off, err + return headerEnd, off, err } - rr.Header().Rdlength = uint16(off - headerEnd) - return off, nil + return headerEnd, off, nil } -func (rr *L64) pack(msg []byte, off int, compression map[string]int, compress bool) (int, error) { - off, err := rr.Hdr.pack(msg, off, compression, compress) +func (rr *L64) pack(msg []byte, off int, compression compressionMap, compress bool) (int, int, error) { + headerEnd, off, err := rr.Hdr.pack(msg, off, compression, compress) if err != nil { - return off, err + return headerEnd, off, err } - headerEnd := off off, err = packUint16(rr.Preference, msg, off) if err != nil { - return off, err + return headerEnd, off, err } off, err = packUint64(rr.Locator64, msg, off) if err != nil { - return off, err + return headerEnd, off, err } - rr.Header().Rdlength = uint16(off - headerEnd) - return off, nil + return headerEnd, off, nil } -func (rr *LOC) pack(msg []byte, off int, compression map[string]int, compress bool) (int, error) { - off, err := rr.Hdr.pack(msg, off, compression, compress) +func (rr *LOC) pack(msg []byte, off int, compression compressionMap, compress bool) (int, int, error) { + headerEnd, off, err := rr.Hdr.pack(msg, off, compression, compress) if err != nil { - return off, err + return headerEnd, off, err } - headerEnd := off off, err = packUint8(rr.Version, msg, off) if err != nil { - return off, err + return headerEnd, off, err } off, err = packUint8(rr.Size, msg, off) if err != nil { - return off, err + return headerEnd, off, err } off, err = packUint8(rr.HorizPre, msg, off) if err != nil { - return off, err + return headerEnd, off, err } off, err = packUint8(rr.VertPre, msg, off) if err != nil { - return off, err + return headerEnd, off, err } off, err = packUint32(rr.Latitude, msg, off) if err != nil { - return off, err + return headerEnd, off, err } off, err = packUint32(rr.Longitude, msg, off) if err != nil { - return off, err + return headerEnd, off, err } off, err = packUint32(rr.Altitude, msg, off) if err != nil { - return off, err + return headerEnd, off, err } - rr.Header().Rdlength = uint16(off - headerEnd) - return off, nil + return headerEnd, off, nil } -func (rr *LP) pack(msg []byte, off int, compression map[string]int, compress bool) (int, error) { - off, err := rr.Hdr.pack(msg, off, compression, compress) +func (rr *LP) pack(msg []byte, off int, compression compressionMap, compress bool) (int, int, error) { + headerEnd, off, err := rr.Hdr.pack(msg, off, compression, compress) if err != nil { - return off, err + return headerEnd, off, err } - headerEnd := off off, err = packUint16(rr.Preference, msg, off) if err != nil { - return off, err + return headerEnd, off, err } - off, err = PackDomainName(rr.Fqdn, msg, off, compression, false) + off, _, err = packDomainName(rr.Fqdn, msg, off, compression, false) if err != nil { - return off, err + return headerEnd, off, err } - rr.Header().Rdlength = uint16(off - headerEnd) - return off, nil + return headerEnd, off, nil } -func (rr *MB) pack(msg []byte, off int, compression map[string]int, compress bool) (int, error) { - off, err := rr.Hdr.pack(msg, off, compression, compress) +func (rr *MB) pack(msg []byte, off int, compression compressionMap, compress bool) (int, int, error) { + headerEnd, off, err := rr.Hdr.pack(msg, off, compression, compress) if err != nil { - return off, err + return headerEnd, off, err } - headerEnd := off - off, err = PackDomainName(rr.Mb, msg, off, compression, compress) + off, _, err = packDomainName(rr.Mb, msg, off, compression, compress) if err != nil { - return off, err + return headerEnd, off, err } - rr.Header().Rdlength = uint16(off - headerEnd) - return off, nil + return headerEnd, off, nil } -func (rr *MD) pack(msg []byte, off int, compression map[string]int, compress bool) (int, error) { - off, err := rr.Hdr.pack(msg, off, compression, compress) +func (rr *MD) pack(msg []byte, off int, compression compressionMap, compress bool) (int, int, error) { + headerEnd, off, err := rr.Hdr.pack(msg, off, compression, compress) if err != nil { - return off, err + return headerEnd, off, err } - headerEnd := off - off, err = PackDomainName(rr.Md, msg, off, compression, compress) + off, _, err = packDomainName(rr.Md, msg, off, compression, compress) if err != nil { - return off, err + return headerEnd, off, err } - rr.Header().Rdlength = uint16(off - headerEnd) - return off, nil + return headerEnd, off, nil } -func (rr *MF) pack(msg []byte, off int, compression map[string]int, compress bool) (int, error) { - off, err := rr.Hdr.pack(msg, off, compression, compress) +func (rr *MF) pack(msg []byte, off int, compression compressionMap, compress bool) (int, int, error) { + headerEnd, off, err := rr.Hdr.pack(msg, off, compression, compress) if err != nil { - return off, err + return headerEnd, off, err } - headerEnd := off - off, err = PackDomainName(rr.Mf, msg, off, compression, compress) + off, _, err = packDomainName(rr.Mf, msg, off, compression, compress) if err != nil { - return off, err + return headerEnd, off, err } - rr.Header().Rdlength = uint16(off - headerEnd) - return off, nil + return headerEnd, off, nil } -func (rr *MG) pack(msg []byte, off int, compression map[string]int, compress bool) (int, error) { - off, err := rr.Hdr.pack(msg, off, compression, compress) +func (rr *MG) pack(msg []byte, off int, compression compressionMap, compress bool) (int, int, error) { + headerEnd, off, err := rr.Hdr.pack(msg, off, compression, compress) if err != nil { - return off, err + return headerEnd, off, err } - headerEnd := off - off, err = PackDomainName(rr.Mg, msg, off, compression, compress) + off, _, err = packDomainName(rr.Mg, msg, off, compression, compress) if err != nil { - return off, err + return headerEnd, off, err } - rr.Header().Rdlength = uint16(off - headerEnd) - return off, nil + return headerEnd, off, nil } -func (rr *MINFO) pack(msg []byte, off int, compression map[string]int, compress bool) (int, error) { - off, err := rr.Hdr.pack(msg, off, compression, compress) +func (rr *MINFO) pack(msg []byte, off int, compression compressionMap, compress bool) (int, int, error) { + headerEnd, off, err := rr.Hdr.pack(msg, off, compression, compress) if err != nil { - return off, err + return headerEnd, off, err } - headerEnd := off - off, err = PackDomainName(rr.Rmail, msg, off, compression, compress) + off, _, err = packDomainName(rr.Rmail, msg, off, compression, compress) if err != nil { - return off, err + return headerEnd, off, err } - off, err = PackDomainName(rr.Email, msg, off, compression, compress) + off, _, err = packDomainName(rr.Email, msg, off, compression, compress) if err != nil { - return off, err + return headerEnd, off, err } - rr.Header().Rdlength = uint16(off - headerEnd) - return off, nil + return headerEnd, off, nil } -func (rr *MR) pack(msg []byte, off int, compression map[string]int, compress bool) (int, error) { - off, err := rr.Hdr.pack(msg, off, compression, compress) +func (rr *MR) pack(msg []byte, off int, compression compressionMap, compress bool) (int, int, error) { + headerEnd, off, err := rr.Hdr.pack(msg, off, compression, compress) if err != nil { - return off, err + return headerEnd, off, err } - headerEnd := off - off, err = PackDomainName(rr.Mr, msg, off, compression, compress) + off, _, err = packDomainName(rr.Mr, msg, off, compression, compress) if err != nil { - return off, err + return headerEnd, off, err } - rr.Header().Rdlength = uint16(off - headerEnd) - return off, nil + return headerEnd, off, nil } -func (rr *MX) pack(msg []byte, off int, compression map[string]int, compress bool) (int, error) { - off, err := rr.Hdr.pack(msg, off, compression, compress) +func (rr *MX) pack(msg []byte, off int, compression compressionMap, compress bool) (int, int, error) { + headerEnd, off, err := rr.Hdr.pack(msg, off, compression, compress) if err != nil { - return off, err + return headerEnd, off, err } - headerEnd := off off, err = packUint16(rr.Preference, msg, off) if err != nil { - return off, err + return headerEnd, off, err } - off, err = PackDomainName(rr.Mx, msg, off, compression, compress) + off, _, err = packDomainName(rr.Mx, msg, off, compression, compress) if err != nil { - return off, err + return headerEnd, off, err } - rr.Header().Rdlength = uint16(off - headerEnd) - return off, nil + return headerEnd, off, nil } -func (rr *NAPTR) pack(msg []byte, off int, compression map[string]int, compress bool) (int, error) { - off, err := rr.Hdr.pack(msg, off, compression, compress) +func (rr *NAPTR) pack(msg []byte, off int, compression compressionMap, compress bool) (int, int, error) { + headerEnd, off, err := rr.Hdr.pack(msg, off, compression, compress) if err != nil { - return off, err + return headerEnd, off, err } - headerEnd := off off, err = packUint16(rr.Order, msg, off) if err != nil { - return off, err + return headerEnd, off, err } off, err = packUint16(rr.Preference, msg, off) if err != nil { - return off, err + return headerEnd, off, err } off, err = packString(rr.Flags, msg, off) if err != nil { - return off, err + return headerEnd, off, err } off, err = packString(rr.Service, msg, off) if err != nil { - return off, err + return headerEnd, off, err } off, err = packString(rr.Regexp, msg, off) if err != nil { - return off, err + return headerEnd, off, err } - off, err = PackDomainName(rr.Replacement, msg, off, compression, false) + off, _, err = packDomainName(rr.Replacement, msg, off, compression, false) if err != nil { - return off, err + return headerEnd, off, err } - rr.Header().Rdlength = uint16(off - headerEnd) - return off, nil + return headerEnd, off, nil } -func (rr *NID) pack(msg []byte, off int, compression map[string]int, compress bool) (int, error) { - off, err := rr.Hdr.pack(msg, off, compression, compress) +func (rr *NID) pack(msg []byte, off int, compression compressionMap, compress bool) (int, int, error) { + headerEnd, off, err := rr.Hdr.pack(msg, off, compression, compress) if err != nil { - return off, err + return headerEnd, off, err } - headerEnd := off off, err = packUint16(rr.Preference, msg, off) if err != nil { - return off, err + return headerEnd, off, err } off, err = packUint64(rr.NodeID, msg, off) if err != nil { - return off, err + return headerEnd, off, err } - rr.Header().Rdlength = uint16(off - headerEnd) - return off, nil + return headerEnd, off, nil } -func (rr *NIMLOC) pack(msg []byte, off int, compression map[string]int, compress bool) (int, error) { - off, err := rr.Hdr.pack(msg, off, compression, compress) +func (rr *NIMLOC) pack(msg []byte, off int, compression compressionMap, compress bool) (int, int, error) { + headerEnd, off, err := rr.Hdr.pack(msg, off, compression, compress) if err != nil { - return off, err + return headerEnd, off, err } - headerEnd := off off, err = packStringHex(rr.Locator, msg, off) if err != nil { - return off, err + return headerEnd, off, err } - rr.Header().Rdlength = uint16(off - headerEnd) - return off, nil + return headerEnd, off, nil } -func (rr *NINFO) pack(msg []byte, off int, compression map[string]int, compress bool) (int, error) { - off, err := rr.Hdr.pack(msg, off, compression, compress) +func (rr *NINFO) pack(msg []byte, off int, compression compressionMap, compress bool) (int, int, error) { + headerEnd, off, err := rr.Hdr.pack(msg, off, compression, compress) if err != nil { - return off, err + return headerEnd, off, err } - headerEnd := off off, err = packStringTxt(rr.ZSData, msg, off) if err != nil { - return off, err + return headerEnd, off, err } - rr.Header().Rdlength = uint16(off - headerEnd) - return off, nil + return headerEnd, off, nil } -func (rr *NS) pack(msg []byte, off int, compression map[string]int, compress bool) (int, error) { - off, err := rr.Hdr.pack(msg, off, compression, compress) +func (rr *NS) pack(msg []byte, off int, compression compressionMap, compress bool) (int, int, error) { + headerEnd, off, err := rr.Hdr.pack(msg, off, compression, compress) if err != nil { - return off, err + return headerEnd, off, err } - headerEnd := off - off, err = PackDomainName(rr.Ns, msg, off, compression, compress) + off, _, err = packDomainName(rr.Ns, msg, off, compression, compress) if err != nil { - return off, err + return headerEnd, off, err } - rr.Header().Rdlength = uint16(off - headerEnd) - return off, nil + return headerEnd, off, nil } -func (rr *NSAPPTR) pack(msg []byte, off int, compression map[string]int, compress bool) (int, error) { - off, err := rr.Hdr.pack(msg, off, compression, compress) +func (rr *NSAPPTR) pack(msg []byte, off int, compression compressionMap, compress bool) (int, int, error) { + headerEnd, off, err := rr.Hdr.pack(msg, off, compression, compress) if err != nil { - return off, err + return headerEnd, off, err } - headerEnd := off - off, err = PackDomainName(rr.Ptr, msg, off, compression, false) + off, _, err = packDomainName(rr.Ptr, msg, off, compression, false) if err != nil { - return off, err + return headerEnd, off, err } - rr.Header().Rdlength = uint16(off - headerEnd) - return off, nil + return headerEnd, off, nil } -func (rr *NSEC) pack(msg []byte, off int, compression map[string]int, compress bool) (int, error) { - off, err := rr.Hdr.pack(msg, off, compression, compress) +func (rr *NSEC) pack(msg []byte, off int, compression compressionMap, compress bool) (int, int, error) { + headerEnd, off, err := rr.Hdr.pack(msg, off, compression, compress) if err != nil { - return off, err + return headerEnd, off, err } - headerEnd := off - off, err = PackDomainName(rr.NextDomain, msg, off, compression, false) + off, _, err = packDomainName(rr.NextDomain, msg, off, compression, false) if err != nil { - return off, err + return headerEnd, off, err } off, err = packDataNsec(rr.TypeBitMap, msg, off) if err != nil { - return off, err + return headerEnd, off, err } - rr.Header().Rdlength = uint16(off - headerEnd) - return off, nil + return headerEnd, off, nil } -func (rr *NSEC3) pack(msg []byte, off int, compression map[string]int, compress bool) (int, error) { - off, err := rr.Hdr.pack(msg, off, compression, compress) +func (rr *NSEC3) pack(msg []byte, off int, compression compressionMap, compress bool) (int, int, error) { + headerEnd, off, err := rr.Hdr.pack(msg, off, compression, compress) if err != nil { - return off, err + return headerEnd, off, err } - headerEnd := off off, err = packUint8(rr.Hash, msg, off) if err != nil { - return off, err + return headerEnd, off, err } off, err = packUint8(rr.Flags, msg, off) if err != nil { - return off, err + return headerEnd, off, err } off, err = packUint16(rr.Iterations, msg, off) if err != nil { - return off, err + return headerEnd, off, err } off, err = packUint8(rr.SaltLength, msg, off) if err != nil { - return off, err + return headerEnd, off, err } // Only pack salt if value is not "-", i.e. empty if rr.Salt != "-" { off, err = packStringHex(rr.Salt, msg, off) if err != nil { - return off, err + return headerEnd, off, err } } off, err = packUint8(rr.HashLength, msg, off) if err != nil { - return off, err + return headerEnd, off, err } off, err = packStringBase32(rr.NextDomain, msg, off) if err != nil { - return off, err + return headerEnd, off, err } off, err = packDataNsec(rr.TypeBitMap, msg, off) if err != nil { - return off, err + return headerEnd, off, err } - rr.Header().Rdlength = uint16(off - headerEnd) - return off, nil + return headerEnd, off, nil } -func (rr *NSEC3PARAM) pack(msg []byte, off int, compression map[string]int, compress bool) (int, error) { - off, err := rr.Hdr.pack(msg, off, compression, compress) +func (rr *NSEC3PARAM) pack(msg []byte, off int, compression compressionMap, compress bool) (int, int, error) { + headerEnd, off, err := rr.Hdr.pack(msg, off, compression, compress) if err != nil { - return off, err + return headerEnd, off, err } - headerEnd := off off, err = packUint8(rr.Hash, msg, off) if err != nil { - return off, err + return headerEnd, off, err } off, err = packUint8(rr.Flags, msg, off) if err != nil { - return off, err + return headerEnd, off, err } off, err = packUint16(rr.Iterations, msg, off) if err != nil { - return off, err + return headerEnd, off, err } off, err = packUint8(rr.SaltLength, msg, off) if err != nil { - return off, err + return headerEnd, off, err } // Only pack salt if value is not "-", i.e. empty if rr.Salt != "-" { off, err = packStringHex(rr.Salt, msg, off) if err != nil { - return off, err + return headerEnd, off, err } } - rr.Header().Rdlength = uint16(off - headerEnd) - return off, nil + return headerEnd, off, nil } -func (rr *OPENPGPKEY) pack(msg []byte, off int, compression map[string]int, compress bool) (int, error) { - off, err := rr.Hdr.pack(msg, off, compression, compress) +func (rr *OPENPGPKEY) pack(msg []byte, off int, compression compressionMap, compress bool) (int, int, error) { + headerEnd, off, err := rr.Hdr.pack(msg, off, compression, compress) if err != nil { - return off, err + return headerEnd, off, err } - headerEnd := off off, err = packStringBase64(rr.PublicKey, msg, off) if err != nil { - return off, err + return headerEnd, off, err } - rr.Header().Rdlength = uint16(off - headerEnd) - return off, nil + return headerEnd, off, nil } -func (rr *OPT) pack(msg []byte, off int, compression map[string]int, compress bool) (int, error) { - off, err := rr.Hdr.pack(msg, off, compression, compress) +func (rr *OPT) pack(msg []byte, off int, compression compressionMap, compress bool) (int, int, error) { + headerEnd, off, err := rr.Hdr.pack(msg, off, compression, compress) if err != nil { - return off, err + return headerEnd, off, err } - headerEnd := off off, err = packDataOpt(rr.Option, msg, off) if err != nil { - return off, err + return headerEnd, off, err } - rr.Header().Rdlength = uint16(off - headerEnd) - return off, nil + return headerEnd, off, nil } -func (rr *PTR) pack(msg []byte, off int, compression map[string]int, compress bool) (int, error) { - off, err := rr.Hdr.pack(msg, off, compression, compress) +func (rr *PTR) pack(msg []byte, off int, compression compressionMap, compress bool) (int, int, error) { + headerEnd, off, err := rr.Hdr.pack(msg, off, compression, compress) if err != nil { - return off, err + return headerEnd, off, err } - headerEnd := off - off, err = PackDomainName(rr.Ptr, msg, off, compression, compress) + off, _, err = packDomainName(rr.Ptr, msg, off, compression, compress) if err != nil { - return off, err + return headerEnd, off, err } - rr.Header().Rdlength = uint16(off - headerEnd) - return off, nil + return headerEnd, off, nil } -func (rr *PX) pack(msg []byte, off int, compression map[string]int, compress bool) (int, error) { - off, err := rr.Hdr.pack(msg, off, compression, compress) +func (rr *PX) pack(msg []byte, off int, compression compressionMap, compress bool) (int, int, error) { + headerEnd, off, err := rr.Hdr.pack(msg, off, compression, compress) if err != nil { - return off, err + return headerEnd, off, err } - headerEnd := off off, err = packUint16(rr.Preference, msg, off) if err != nil { - return off, err + return headerEnd, off, err } - off, err = PackDomainName(rr.Map822, msg, off, compression, false) + off, _, err = packDomainName(rr.Map822, msg, off, compression, false) if err != nil { - return off, err + return headerEnd, off, err } - off, err = PackDomainName(rr.Mapx400, msg, off, compression, false) + off, _, err = packDomainName(rr.Mapx400, msg, off, compression, false) if err != nil { - return off, err + return headerEnd, off, err } - rr.Header().Rdlength = uint16(off - headerEnd) - return off, nil + return headerEnd, off, nil } -func (rr *RFC3597) pack(msg []byte, off int, compression map[string]int, compress bool) (int, error) { - off, err := rr.Hdr.pack(msg, off, compression, compress) +func (rr *RFC3597) pack(msg []byte, off int, compression compressionMap, compress bool) (int, int, error) { + headerEnd, off, err := rr.Hdr.pack(msg, off, compression, compress) if err != nil { - return off, err + return headerEnd, off, err } - headerEnd := off off, err = packStringHex(rr.Rdata, msg, off) if err != nil { - return off, err + return headerEnd, off, err } - rr.Header().Rdlength = uint16(off - headerEnd) - return off, nil + return headerEnd, off, nil } -func (rr *RKEY) pack(msg []byte, off int, compression map[string]int, compress bool) (int, error) { - off, err := rr.Hdr.pack(msg, off, compression, compress) +func (rr *RKEY) pack(msg []byte, off int, compression compressionMap, compress bool) (int, int, error) { + headerEnd, off, err := rr.Hdr.pack(msg, off, compression, compress) if err != nil { - return off, err + return headerEnd, off, err } - headerEnd := off off, err = packUint16(rr.Flags, msg, off) if err != nil { - return off, err + return headerEnd, off, err } off, err = packUint8(rr.Protocol, msg, off) if err != nil { - return off, err + return headerEnd, off, err } off, err = packUint8(rr.Algorithm, msg, off) if err != nil { - return off, err + return headerEnd, off, err } off, err = packStringBase64(rr.PublicKey, msg, off) if err != nil { - return off, err + return headerEnd, off, err } - rr.Header().Rdlength = uint16(off - headerEnd) - return off, nil + return headerEnd, off, nil } -func (rr *RP) pack(msg []byte, off int, compression map[string]int, compress bool) (int, error) { - off, err := rr.Hdr.pack(msg, off, compression, compress) +func (rr *RP) pack(msg []byte, off int, compression compressionMap, compress bool) (int, int, error) { + headerEnd, off, err := rr.Hdr.pack(msg, off, compression, compress) if err != nil { - return off, err + return headerEnd, off, err } - headerEnd := off - off, err = PackDomainName(rr.Mbox, msg, off, compression, false) + off, _, err = packDomainName(rr.Mbox, msg, off, compression, false) if err != nil { - return off, err + return headerEnd, off, err } - off, err = PackDomainName(rr.Txt, msg, off, compression, false) + off, _, err = packDomainName(rr.Txt, msg, off, compression, false) if err != nil { - return off, err + return headerEnd, off, err } - rr.Header().Rdlength = uint16(off - headerEnd) - return off, nil + return headerEnd, off, nil } -func (rr *RRSIG) pack(msg []byte, off int, compression map[string]int, compress bool) (int, error) { - off, err := rr.Hdr.pack(msg, off, compression, compress) +func (rr *RRSIG) pack(msg []byte, off int, compression compressionMap, compress bool) (int, int, error) { + headerEnd, off, err := rr.Hdr.pack(msg, off, compression, compress) if err != nil { - return off, err + return headerEnd, off, err } - headerEnd := off off, err = packUint16(rr.TypeCovered, msg, off) if err != nil { - return off, err + return headerEnd, off, err } off, err = packUint8(rr.Algorithm, msg, off) if err != nil { - return off, err + return headerEnd, off, err } off, err = packUint8(rr.Labels, msg, off) if err != nil { - return off, err + return headerEnd, off, err } off, err = packUint32(rr.OrigTtl, msg, off) if err != nil { - return off, err + return headerEnd, off, err } off, err = packUint32(rr.Expiration, msg, off) if err != nil { - return off, err + return headerEnd, off, err } off, err = packUint32(rr.Inception, msg, off) if err != nil { - return off, err + return headerEnd, off, err } off, err = packUint16(rr.KeyTag, msg, off) if err != nil { - return off, err + return headerEnd, off, err } - off, err = PackDomainName(rr.SignerName, msg, off, compression, false) + off, _, err = packDomainName(rr.SignerName, msg, off, compression, false) if err != nil { - return off, err + return headerEnd, off, err } off, err = packStringBase64(rr.Signature, msg, off) if err != nil { - return off, err + return headerEnd, off, err } - rr.Header().Rdlength = uint16(off - headerEnd) - return off, nil + return headerEnd, off, nil } -func (rr *RT) pack(msg []byte, off int, compression map[string]int, compress bool) (int, error) { - off, err := rr.Hdr.pack(msg, off, compression, compress) +func (rr *RT) pack(msg []byte, off int, compression compressionMap, compress bool) (int, int, error) { + headerEnd, off, err := rr.Hdr.pack(msg, off, compression, compress) if err != nil { - return off, err + return headerEnd, off, err } - headerEnd := off off, err = packUint16(rr.Preference, msg, off) if err != nil { - return off, err + return headerEnd, off, err } - off, err = PackDomainName(rr.Host, msg, off, compression, compress) + off, _, err = packDomainName(rr.Host, msg, off, compression, false) if err != nil { - return off, err + return headerEnd, off, err } - rr.Header().Rdlength = uint16(off - headerEnd) - return off, nil + return headerEnd, off, nil } -func (rr *SIG) pack(msg []byte, off int, compression map[string]int, compress bool) (int, error) { - off, err := rr.Hdr.pack(msg, off, compression, compress) +func (rr *SIG) pack(msg []byte, off int, compression compressionMap, compress bool) (int, int, error) { + headerEnd, off, err := rr.Hdr.pack(msg, off, compression, compress) if err != nil { - return off, err + return headerEnd, off, err } - headerEnd := off off, err = packUint16(rr.TypeCovered, msg, off) if err != nil { - return off, err + return headerEnd, off, err } off, err = packUint8(rr.Algorithm, msg, off) if err != nil { - return off, err + return headerEnd, off, err } off, err = packUint8(rr.Labels, msg, off) if err != nil { - return off, err + return headerEnd, off, err } off, err = packUint32(rr.OrigTtl, msg, off) if err != nil { - return off, err + return headerEnd, off, err } off, err = packUint32(rr.Expiration, msg, off) if err != nil { - return off, err + return headerEnd, off, err } off, err = packUint32(rr.Inception, msg, off) if err != nil { - return off, err + return headerEnd, off, err } off, err = packUint16(rr.KeyTag, msg, off) if err != nil { - return off, err + return headerEnd, off, err } - off, err = PackDomainName(rr.SignerName, msg, off, compression, false) + off, _, err = packDomainName(rr.SignerName, msg, off, compression, false) if err != nil { - return off, err + return headerEnd, off, err } off, err = packStringBase64(rr.Signature, msg, off) if err != nil { - return off, err + return headerEnd, off, err } - rr.Header().Rdlength = uint16(off - headerEnd) - return off, nil + return headerEnd, off, nil } -func (rr *SMIMEA) pack(msg []byte, off int, compression map[string]int, compress bool) (int, error) { - off, err := rr.Hdr.pack(msg, off, compression, compress) +func (rr *SMIMEA) pack(msg []byte, off int, compression compressionMap, compress bool) (int, int, error) { + headerEnd, off, err := rr.Hdr.pack(msg, off, compression, compress) if err != nil { - return off, err + return headerEnd, off, err } - headerEnd := off off, err = packUint8(rr.Usage, msg, off) if err != nil { - return off, err + return headerEnd, off, err } off, err = packUint8(rr.Selector, msg, off) if err != nil { - return off, err + return headerEnd, off, err } off, err = packUint8(rr.MatchingType, msg, off) if err != nil { - return off, err + return headerEnd, off, err } off, err = packStringHex(rr.Certificate, msg, off) if err != nil { - return off, err + return headerEnd, off, err } - rr.Header().Rdlength = uint16(off - headerEnd) - return off, nil + return headerEnd, off, nil } -func (rr *SOA) pack(msg []byte, off int, compression map[string]int, compress bool) (int, error) { - off, err := rr.Hdr.pack(msg, off, compression, compress) +func (rr *SOA) pack(msg []byte, off int, compression compressionMap, compress bool) (int, int, error) { + headerEnd, off, err := rr.Hdr.pack(msg, off, compression, compress) if err != nil { - return off, err + return headerEnd, off, err } - headerEnd := off - off, err = PackDomainName(rr.Ns, msg, off, compression, compress) + off, _, err = packDomainName(rr.Ns, msg, off, compression, compress) if err != nil { - return off, err + return headerEnd, off, err } - off, err = PackDomainName(rr.Mbox, msg, off, compression, compress) + off, _, err = packDomainName(rr.Mbox, msg, off, compression, compress) if err != nil { - return off, err + return headerEnd, off, err } off, err = packUint32(rr.Serial, msg, off) if err != nil { - return off, err + return headerEnd, off, err } off, err = packUint32(rr.Refresh, msg, off) if err != nil { - return off, err + return headerEnd, off, err } off, err = packUint32(rr.Retry, msg, off) if err != nil { - return off, err + return headerEnd, off, err } off, err = packUint32(rr.Expire, msg, off) if err != nil { - return off, err + return headerEnd, off, err } off, err = packUint32(rr.Minttl, msg, off) if err != nil { - return off, err + return headerEnd, off, err } - rr.Header().Rdlength = uint16(off - headerEnd) - return off, nil + return headerEnd, off, nil } -func (rr *SPF) pack(msg []byte, off int, compression map[string]int, compress bool) (int, error) { - off, err := rr.Hdr.pack(msg, off, compression, compress) +func (rr *SPF) pack(msg []byte, off int, compression compressionMap, compress bool) (int, int, error) { + headerEnd, off, err := rr.Hdr.pack(msg, off, compression, compress) if err != nil { - return off, err + return headerEnd, off, err } - headerEnd := off off, err = packStringTxt(rr.Txt, msg, off) if err != nil { - return off, err + return headerEnd, off, err } - rr.Header().Rdlength = uint16(off - headerEnd) - return off, nil + return headerEnd, off, nil } -func (rr *SRV) pack(msg []byte, off int, compression map[string]int, compress bool) (int, error) { - off, err := rr.Hdr.pack(msg, off, compression, compress) +func (rr *SRV) pack(msg []byte, off int, compression compressionMap, compress bool) (int, int, error) { + headerEnd, off, err := rr.Hdr.pack(msg, off, compression, compress) if err != nil { - return off, err + return headerEnd, off, err } - headerEnd := off off, err = packUint16(rr.Priority, msg, off) if err != nil { - return off, err + return headerEnd, off, err } off, err = packUint16(rr.Weight, msg, off) if err != nil { - return off, err + return headerEnd, off, err } off, err = packUint16(rr.Port, msg, off) if err != nil { - return off, err + return headerEnd, off, err } - off, err = PackDomainName(rr.Target, msg, off, compression, false) + off, _, err = packDomainName(rr.Target, msg, off, compression, false) if err != nil { - return off, err + return headerEnd, off, err } - rr.Header().Rdlength = uint16(off - headerEnd) - return off, nil + return headerEnd, off, nil } -func (rr *SSHFP) pack(msg []byte, off int, compression map[string]int, compress bool) (int, error) { - off, err := rr.Hdr.pack(msg, off, compression, compress) +func (rr *SSHFP) pack(msg []byte, off int, compression compressionMap, compress bool) (int, int, error) { + headerEnd, off, err := rr.Hdr.pack(msg, off, compression, compress) if err != nil { - return off, err + return headerEnd, off, err } - headerEnd := off off, err = packUint8(rr.Algorithm, msg, off) if err != nil { - return off, err + return headerEnd, off, err } off, err = packUint8(rr.Type, msg, off) if err != nil { - return off, err + return headerEnd, off, err } off, err = packStringHex(rr.FingerPrint, msg, off) if err != nil { - return off, err + return headerEnd, off, err } - rr.Header().Rdlength = uint16(off - headerEnd) - return off, nil + return headerEnd, off, nil } -func (rr *TA) pack(msg []byte, off int, compression map[string]int, compress bool) (int, error) { - off, err := rr.Hdr.pack(msg, off, compression, compress) +func (rr *TA) pack(msg []byte, off int, compression compressionMap, compress bool) (int, int, error) { + headerEnd, off, err := rr.Hdr.pack(msg, off, compression, compress) if err != nil { - return off, err + return headerEnd, off, err } - headerEnd := off off, err = packUint16(rr.KeyTag, msg, off) if err != nil { - return off, err + return headerEnd, off, err } off, err = packUint8(rr.Algorithm, msg, off) if err != nil { - return off, err + return headerEnd, off, err } off, err = packUint8(rr.DigestType, msg, off) if err != nil { - return off, err + return headerEnd, off, err } off, err = packStringHex(rr.Digest, msg, off) if err != nil { - return off, err + return headerEnd, off, err } - rr.Header().Rdlength = uint16(off - headerEnd) - return off, nil + return headerEnd, off, nil } -func (rr *TALINK) pack(msg []byte, off int, compression map[string]int, compress bool) (int, error) { - off, err := rr.Hdr.pack(msg, off, compression, compress) +func (rr *TALINK) pack(msg []byte, off int, compression compressionMap, compress bool) (int, int, error) { + headerEnd, off, err := rr.Hdr.pack(msg, off, compression, compress) if err != nil { - return off, err + return headerEnd, off, err } - headerEnd := off - off, err = PackDomainName(rr.PreviousName, msg, off, compression, false) + off, _, err = packDomainName(rr.PreviousName, msg, off, compression, false) if err != nil { - return off, err + return headerEnd, off, err } - off, err = PackDomainName(rr.NextName, msg, off, compression, false) + off, _, err = packDomainName(rr.NextName, msg, off, compression, false) if err != nil { - return off, err + return headerEnd, off, err } - rr.Header().Rdlength = uint16(off - headerEnd) - return off, nil + return headerEnd, off, nil } -func (rr *TKEY) pack(msg []byte, off int, compression map[string]int, compress bool) (int, error) { - off, err := rr.Hdr.pack(msg, off, compression, compress) +func (rr *TKEY) pack(msg []byte, off int, compression compressionMap, compress bool) (int, int, error) { + headerEnd, off, err := rr.Hdr.pack(msg, off, compression, compress) if err != nil { - return off, err + return headerEnd, off, err } - headerEnd := off - off, err = PackDomainName(rr.Algorithm, msg, off, compression, false) + off, _, err = packDomainName(rr.Algorithm, msg, off, compression, false) if err != nil { - return off, err + return headerEnd, off, err } off, err = packUint32(rr.Inception, msg, off) if err != nil { - return off, err + return headerEnd, off, err } off, err = packUint32(rr.Expiration, msg, off) if err != nil { - return off, err + return headerEnd, off, err } off, err = packUint16(rr.Mode, msg, off) if err != nil { - return off, err + return headerEnd, off, err } off, err = packUint16(rr.Error, msg, off) if err != nil { - return off, err + return headerEnd, off, err } off, err = packUint16(rr.KeySize, msg, off) if err != nil { - return off, err + return headerEnd, off, err } off, err = packStringHex(rr.Key, msg, off) if err != nil { - return off, err + return headerEnd, off, err } off, err = packUint16(rr.OtherLen, msg, off) if err != nil { - return off, err + return headerEnd, off, err } off, err = packStringHex(rr.OtherData, msg, off) if err != nil { - return off, err + return headerEnd, off, err } - rr.Header().Rdlength = uint16(off - headerEnd) - return off, nil + return headerEnd, off, nil } -func (rr *TLSA) pack(msg []byte, off int, compression map[string]int, compress bool) (int, error) { - off, err := rr.Hdr.pack(msg, off, compression, compress) +func (rr *TLSA) pack(msg []byte, off int, compression compressionMap, compress bool) (int, int, error) { + headerEnd, off, err := rr.Hdr.pack(msg, off, compression, compress) if err != nil { - return off, err + return headerEnd, off, err } - headerEnd := off off, err = packUint8(rr.Usage, msg, off) if err != nil { - return off, err + return headerEnd, off, err } off, err = packUint8(rr.Selector, msg, off) if err != nil { - return off, err + return headerEnd, off, err } off, err = packUint8(rr.MatchingType, msg, off) if err != nil { - return off, err + return headerEnd, off, err } off, err = packStringHex(rr.Certificate, msg, off) if err != nil { - return off, err + return headerEnd, off, err } - rr.Header().Rdlength = uint16(off - headerEnd) - return off, nil + return headerEnd, off, nil } -func (rr *TSIG) pack(msg []byte, off int, compression map[string]int, compress bool) (int, error) { - off, err := rr.Hdr.pack(msg, off, compression, compress) +func (rr *TSIG) pack(msg []byte, off int, compression compressionMap, compress bool) (int, int, error) { + headerEnd, off, err := rr.Hdr.pack(msg, off, compression, compress) if err != nil { - return off, err + return headerEnd, off, err } - headerEnd := off - off, err = PackDomainName(rr.Algorithm, msg, off, compression, false) + off, _, err = packDomainName(rr.Algorithm, msg, off, compression, false) if err != nil { - return off, err + return headerEnd, off, err } off, err = packUint48(rr.TimeSigned, msg, off) if err != nil { - return off, err + return headerEnd, off, err } off, err = packUint16(rr.Fudge, msg, off) if err != nil { - return off, err + return headerEnd, off, err } off, err = packUint16(rr.MACSize, msg, off) if err != nil { - return off, err + return headerEnd, off, err } off, err = packStringHex(rr.MAC, msg, off) if err != nil { - return off, err + return headerEnd, off, err } off, err = packUint16(rr.OrigId, msg, off) if err != nil { - return off, err + return headerEnd, off, err } off, err = packUint16(rr.Error, msg, off) if err != nil { - return off, err + return headerEnd, off, err } off, err = packUint16(rr.OtherLen, msg, off) if err != nil { - return off, err + return headerEnd, off, err } off, err = packStringHex(rr.OtherData, msg, off) if err != nil { - return off, err + return headerEnd, off, err } - rr.Header().Rdlength = uint16(off - headerEnd) - return off, nil + return headerEnd, off, nil } -func (rr *TXT) pack(msg []byte, off int, compression map[string]int, compress bool) (int, error) { - off, err := rr.Hdr.pack(msg, off, compression, compress) +func (rr *TXT) pack(msg []byte, off int, compression compressionMap, compress bool) (int, int, error) { + headerEnd, off, err := rr.Hdr.pack(msg, off, compression, compress) if err != nil { - return off, err + return headerEnd, off, err } - headerEnd := off off, err = packStringTxt(rr.Txt, msg, off) if err != nil { - return off, err + return headerEnd, off, err } - rr.Header().Rdlength = uint16(off - headerEnd) - return off, nil + return headerEnd, off, nil } -func (rr *UID) pack(msg []byte, off int, compression map[string]int, compress bool) (int, error) { - off, err := rr.Hdr.pack(msg, off, compression, compress) +func (rr *UID) pack(msg []byte, off int, compression compressionMap, compress bool) (int, int, error) { + headerEnd, off, err := rr.Hdr.pack(msg, off, compression, compress) if err != nil { - return off, err + return headerEnd, off, err } - headerEnd := off off, err = packUint32(rr.Uid, msg, off) if err != nil { - return off, err + return headerEnd, off, err } - rr.Header().Rdlength = uint16(off - headerEnd) - return off, nil + return headerEnd, off, nil } -func (rr *UINFO) pack(msg []byte, off int, compression map[string]int, compress bool) (int, error) { - off, err := rr.Hdr.pack(msg, off, compression, compress) +func (rr *UINFO) pack(msg []byte, off int, compression compressionMap, compress bool) (int, int, error) { + headerEnd, off, err := rr.Hdr.pack(msg, off, compression, compress) if err != nil { - return off, err + return headerEnd, off, err } - headerEnd := off off, err = packString(rr.Uinfo, msg, off) if err != nil { - return off, err + return headerEnd, off, err } - rr.Header().Rdlength = uint16(off - headerEnd) - return off, nil + return headerEnd, off, nil } -func (rr *URI) pack(msg []byte, off int, compression map[string]int, compress bool) (int, error) { - off, err := rr.Hdr.pack(msg, off, compression, compress) +func (rr *URI) pack(msg []byte, off int, compression compressionMap, compress bool) (int, int, error) { + headerEnd, off, err := rr.Hdr.pack(msg, off, compression, compress) if err != nil { - return off, err + return headerEnd, off, err } - headerEnd := off off, err = packUint16(rr.Priority, msg, off) if err != nil { - return off, err + return headerEnd, off, err } off, err = packUint16(rr.Weight, msg, off) if err != nil { - return off, err + return headerEnd, off, err } off, err = packStringOctet(rr.Target, msg, off) if err != nil { - return off, err + return headerEnd, off, err } - rr.Header().Rdlength = uint16(off - headerEnd) - return off, nil + return headerEnd, off, nil } -func (rr *X25) pack(msg []byte, off int, compression map[string]int, compress bool) (int, error) { - off, err := rr.Hdr.pack(msg, off, compression, compress) +func (rr *X25) pack(msg []byte, off int, compression compressionMap, compress bool) (int, int, error) { + headerEnd, off, err := rr.Hdr.pack(msg, off, compression, compress) if err != nil { - return off, err + return headerEnd, off, err } - headerEnd := off off, err = packString(rr.PSDNAddress, msg, off) if err != nil { - return off, err + return headerEnd, off, err } - rr.Header().Rdlength = uint16(off - headerEnd) - return off, nil + return headerEnd, off, nil } // unpack*() functions diff --git a/vendor/github.com/miekg/dns/ztypes.go b/vendor/github.com/miekg/dns/ztypes.go index 965753b11..71662b7b7 100644 --- a/vendor/github.com/miekg/dns/ztypes.go +++ b/vendor/github.com/miekg/dns/ztypes.go @@ -236,144 +236,144 @@ func (rr *URI) Header() *RR_Header { return &rr.Hdr } func (rr *X25) Header() *RR_Header { return &rr.Hdr } // len() functions -func (rr *A) len() int { - l := rr.Hdr.len() +func (rr *A) len(off int, compression map[string]struct{}) int { + l := rr.Hdr.len(off, compression) l += net.IPv4len // A return l } -func (rr *AAAA) len() int { - l := rr.Hdr.len() +func (rr *AAAA) len(off int, compression map[string]struct{}) int { + l := rr.Hdr.len(off, compression) l += net.IPv6len // AAAA return l } -func (rr *AFSDB) len() int { - l := rr.Hdr.len() +func (rr *AFSDB) len(off int, compression map[string]struct{}) int { + l := rr.Hdr.len(off, compression) l += 2 // Subtype - l += len(rr.Hostname) + 1 + l += domainNameLen(rr.Hostname, off+l, compression, false) return l } -func (rr *ANY) len() int { - l := rr.Hdr.len() +func (rr *ANY) len(off int, compression map[string]struct{}) int { + l := rr.Hdr.len(off, compression) return l } -func (rr *AVC) len() int { - l := rr.Hdr.len() +func (rr *AVC) len(off int, compression map[string]struct{}) int { + l := rr.Hdr.len(off, compression) for _, x := range rr.Txt { l += len(x) + 1 } return l } -func (rr *CAA) len() int { - l := rr.Hdr.len() +func (rr *CAA) len(off int, compression map[string]struct{}) int { + l := rr.Hdr.len(off, compression) l++ // Flag l += len(rr.Tag) + 1 l += len(rr.Value) return l } -func (rr *CERT) len() int { - l := rr.Hdr.len() +func (rr *CERT) len(off int, compression map[string]struct{}) int { + l := rr.Hdr.len(off, compression) l += 2 // Type l += 2 // KeyTag l++ // Algorithm l += base64.StdEncoding.DecodedLen(len(rr.Certificate)) return l } -func (rr *CNAME) len() int { - l := rr.Hdr.len() - l += len(rr.Target) + 1 +func (rr *CNAME) len(off int, compression map[string]struct{}) int { + l := rr.Hdr.len(off, compression) + l += domainNameLen(rr.Target, off+l, compression, true) return l } -func (rr *DHCID) len() int { - l := rr.Hdr.len() +func (rr *DHCID) len(off int, compression map[string]struct{}) int { + l := rr.Hdr.len(off, compression) l += base64.StdEncoding.DecodedLen(len(rr.Digest)) return l } -func (rr *DNAME) len() int { - l := rr.Hdr.len() - l += len(rr.Target) + 1 +func (rr *DNAME) len(off int, compression map[string]struct{}) int { + l := rr.Hdr.len(off, compression) + l += domainNameLen(rr.Target, off+l, compression, false) return l } -func (rr *DNSKEY) len() int { - l := rr.Hdr.len() +func (rr *DNSKEY) len(off int, compression map[string]struct{}) int { + l := rr.Hdr.len(off, compression) l += 2 // Flags l++ // Protocol l++ // Algorithm l += base64.StdEncoding.DecodedLen(len(rr.PublicKey)) return l } -func (rr *DS) len() int { - l := rr.Hdr.len() +func (rr *DS) len(off int, compression map[string]struct{}) int { + l := rr.Hdr.len(off, compression) l += 2 // KeyTag l++ // Algorithm l++ // DigestType l += len(rr.Digest)/2 + 1 return l } -func (rr *EID) len() int { - l := rr.Hdr.len() +func (rr *EID) len(off int, compression map[string]struct{}) int { + l := rr.Hdr.len(off, compression) l += len(rr.Endpoint)/2 + 1 return l } -func (rr *EUI48) len() int { - l := rr.Hdr.len() +func (rr *EUI48) len(off int, compression map[string]struct{}) int { + l := rr.Hdr.len(off, compression) l += 6 // Address return l } -func (rr *EUI64) len() int { - l := rr.Hdr.len() +func (rr *EUI64) len(off int, compression map[string]struct{}) int { + l := rr.Hdr.len(off, compression) l += 8 // Address return l } -func (rr *GID) len() int { - l := rr.Hdr.len() +func (rr *GID) len(off int, compression map[string]struct{}) int { + l := rr.Hdr.len(off, compression) l += 4 // Gid return l } -func (rr *GPOS) len() int { - l := rr.Hdr.len() +func (rr *GPOS) len(off int, compression map[string]struct{}) int { + l := rr.Hdr.len(off, compression) l += len(rr.Longitude) + 1 l += len(rr.Latitude) + 1 l += len(rr.Altitude) + 1 return l } -func (rr *HINFO) len() int { - l := rr.Hdr.len() +func (rr *HINFO) len(off int, compression map[string]struct{}) int { + l := rr.Hdr.len(off, compression) l += len(rr.Cpu) + 1 l += len(rr.Os) + 1 return l } -func (rr *HIP) len() int { - l := rr.Hdr.len() +func (rr *HIP) len(off int, compression map[string]struct{}) int { + l := rr.Hdr.len(off, compression) l++ // HitLength l++ // PublicKeyAlgorithm l += 2 // PublicKeyLength l += len(rr.Hit) / 2 l += base64.StdEncoding.DecodedLen(len(rr.PublicKey)) for _, x := range rr.RendezvousServers { - l += len(x) + 1 + l += domainNameLen(x, off+l, compression, false) } return l } -func (rr *KX) len() int { - l := rr.Hdr.len() +func (rr *KX) len(off int, compression map[string]struct{}) int { + l := rr.Hdr.len(off, compression) l += 2 // Preference - l += len(rr.Exchanger) + 1 + l += domainNameLen(rr.Exchanger, off+l, compression, false) return l } -func (rr *L32) len() int { - l := rr.Hdr.len() +func (rr *L32) len(off int, compression map[string]struct{}) int { + l := rr.Hdr.len(off, compression) l += 2 // Preference l += net.IPv4len // Locator32 return l } -func (rr *L64) len() int { - l := rr.Hdr.len() +func (rr *L64) len(off int, compression map[string]struct{}) int { + l := rr.Hdr.len(off, compression) l += 2 // Preference l += 8 // Locator64 return l } -func (rr *LOC) len() int { - l := rr.Hdr.len() +func (rr *LOC) len(off int, compression map[string]struct{}) int { + l := rr.Hdr.len(off, compression) l++ // Version l++ // Size l++ // HorizPre @@ -383,89 +383,89 @@ func (rr *LOC) len() int { l += 4 // Altitude return l } -func (rr *LP) len() int { - l := rr.Hdr.len() +func (rr *LP) len(off int, compression map[string]struct{}) int { + l := rr.Hdr.len(off, compression) l += 2 // Preference - l += len(rr.Fqdn) + 1 + l += domainNameLen(rr.Fqdn, off+l, compression, false) return l } -func (rr *MB) len() int { - l := rr.Hdr.len() - l += len(rr.Mb) + 1 +func (rr *MB) len(off int, compression map[string]struct{}) int { + l := rr.Hdr.len(off, compression) + l += domainNameLen(rr.Mb, off+l, compression, true) return l } -func (rr *MD) len() int { - l := rr.Hdr.len() - l += len(rr.Md) + 1 +func (rr *MD) len(off int, compression map[string]struct{}) int { + l := rr.Hdr.len(off, compression) + l += domainNameLen(rr.Md, off+l, compression, true) return l } -func (rr *MF) len() int { - l := rr.Hdr.len() - l += len(rr.Mf) + 1 +func (rr *MF) len(off int, compression map[string]struct{}) int { + l := rr.Hdr.len(off, compression) + l += domainNameLen(rr.Mf, off+l, compression, true) return l } -func (rr *MG) len() int { - l := rr.Hdr.len() - l += len(rr.Mg) + 1 +func (rr *MG) len(off int, compression map[string]struct{}) int { + l := rr.Hdr.len(off, compression) + l += domainNameLen(rr.Mg, off+l, compression, true) return l } -func (rr *MINFO) len() int { - l := rr.Hdr.len() - l += len(rr.Rmail) + 1 - l += len(rr.Email) + 1 +func (rr *MINFO) len(off int, compression map[string]struct{}) int { + l := rr.Hdr.len(off, compression) + l += domainNameLen(rr.Rmail, off+l, compression, true) + l += domainNameLen(rr.Email, off+l, compression, true) return l } -func (rr *MR) len() int { - l := rr.Hdr.len() - l += len(rr.Mr) + 1 +func (rr *MR) len(off int, compression map[string]struct{}) int { + l := rr.Hdr.len(off, compression) + l += domainNameLen(rr.Mr, off+l, compression, true) return l } -func (rr *MX) len() int { - l := rr.Hdr.len() +func (rr *MX) len(off int, compression map[string]struct{}) int { + l := rr.Hdr.len(off, compression) l += 2 // Preference - l += len(rr.Mx) + 1 + l += domainNameLen(rr.Mx, off+l, compression, true) return l } -func (rr *NAPTR) len() int { - l := rr.Hdr.len() +func (rr *NAPTR) len(off int, compression map[string]struct{}) int { + l := rr.Hdr.len(off, compression) l += 2 // Order l += 2 // Preference l += len(rr.Flags) + 1 l += len(rr.Service) + 1 l += len(rr.Regexp) + 1 - l += len(rr.Replacement) + 1 + l += domainNameLen(rr.Replacement, off+l, compression, false) return l } -func (rr *NID) len() int { - l := rr.Hdr.len() +func (rr *NID) len(off int, compression map[string]struct{}) int { + l := rr.Hdr.len(off, compression) l += 2 // Preference l += 8 // NodeID return l } -func (rr *NIMLOC) len() int { - l := rr.Hdr.len() +func (rr *NIMLOC) len(off int, compression map[string]struct{}) int { + l := rr.Hdr.len(off, compression) l += len(rr.Locator)/2 + 1 return l } -func (rr *NINFO) len() int { - l := rr.Hdr.len() +func (rr *NINFO) len(off int, compression map[string]struct{}) int { + l := rr.Hdr.len(off, compression) for _, x := range rr.ZSData { l += len(x) + 1 } return l } -func (rr *NS) len() int { - l := rr.Hdr.len() - l += len(rr.Ns) + 1 +func (rr *NS) len(off int, compression map[string]struct{}) int { + l := rr.Hdr.len(off, compression) + l += domainNameLen(rr.Ns, off+l, compression, true) return l } -func (rr *NSAPPTR) len() int { - l := rr.Hdr.len() - l += len(rr.Ptr) + 1 +func (rr *NSAPPTR) len(off int, compression map[string]struct{}) int { + l := rr.Hdr.len(off, compression) + l += domainNameLen(rr.Ptr, off+l, compression, false) return l } -func (rr *NSEC3PARAM) len() int { - l := rr.Hdr.len() +func (rr *NSEC3PARAM) len(off int, compression map[string]struct{}) int { + l := rr.Hdr.len(off, compression) l++ // Hash l++ // Flags l += 2 // Iterations @@ -473,44 +473,44 @@ func (rr *NSEC3PARAM) len() int { l += len(rr.Salt) / 2 return l } -func (rr *OPENPGPKEY) len() int { - l := rr.Hdr.len() +func (rr *OPENPGPKEY) len(off int, compression map[string]struct{}) int { + l := rr.Hdr.len(off, compression) l += base64.StdEncoding.DecodedLen(len(rr.PublicKey)) return l } -func (rr *PTR) len() int { - l := rr.Hdr.len() - l += len(rr.Ptr) + 1 +func (rr *PTR) len(off int, compression map[string]struct{}) int { + l := rr.Hdr.len(off, compression) + l += domainNameLen(rr.Ptr, off+l, compression, true) return l } -func (rr *PX) len() int { - l := rr.Hdr.len() +func (rr *PX) len(off int, compression map[string]struct{}) int { + l := rr.Hdr.len(off, compression) l += 2 // Preference - l += len(rr.Map822) + 1 - l += len(rr.Mapx400) + 1 + l += domainNameLen(rr.Map822, off+l, compression, false) + l += domainNameLen(rr.Mapx400, off+l, compression, false) return l } -func (rr *RFC3597) len() int { - l := rr.Hdr.len() +func (rr *RFC3597) len(off int, compression map[string]struct{}) int { + l := rr.Hdr.len(off, compression) l += len(rr.Rdata)/2 + 1 return l } -func (rr *RKEY) len() int { - l := rr.Hdr.len() +func (rr *RKEY) len(off int, compression map[string]struct{}) int { + l := rr.Hdr.len(off, compression) l += 2 // Flags l++ // Protocol l++ // Algorithm l += base64.StdEncoding.DecodedLen(len(rr.PublicKey)) return l } -func (rr *RP) len() int { - l := rr.Hdr.len() - l += len(rr.Mbox) + 1 - l += len(rr.Txt) + 1 +func (rr *RP) len(off int, compression map[string]struct{}) int { + l := rr.Hdr.len(off, compression) + l += domainNameLen(rr.Mbox, off+l, compression, false) + l += domainNameLen(rr.Txt, off+l, compression, false) return l } -func (rr *RRSIG) len() int { - l := rr.Hdr.len() +func (rr *RRSIG) len(off int, compression map[string]struct{}) int { + l := rr.Hdr.len(off, compression) l += 2 // TypeCovered l++ // Algorithm l++ // Labels @@ -518,28 +518,28 @@ func (rr *RRSIG) len() int { l += 4 // Expiration l += 4 // Inception l += 2 // KeyTag - l += len(rr.SignerName) + 1 + l += domainNameLen(rr.SignerName, off+l, compression, false) l += base64.StdEncoding.DecodedLen(len(rr.Signature)) return l } -func (rr *RT) len() int { - l := rr.Hdr.len() +func (rr *RT) len(off int, compression map[string]struct{}) int { + l := rr.Hdr.len(off, compression) l += 2 // Preference - l += len(rr.Host) + 1 + l += domainNameLen(rr.Host, off+l, compression, false) return l } -func (rr *SMIMEA) len() int { - l := rr.Hdr.len() +func (rr *SMIMEA) len(off int, compression map[string]struct{}) int { + l := rr.Hdr.len(off, compression) l++ // Usage l++ // Selector l++ // MatchingType l += len(rr.Certificate)/2 + 1 return l } -func (rr *SOA) len() int { - l := rr.Hdr.len() - l += len(rr.Ns) + 1 - l += len(rr.Mbox) + 1 +func (rr *SOA) len(off int, compression map[string]struct{}) int { + l := rr.Hdr.len(off, compression) + l += domainNameLen(rr.Ns, off+l, compression, true) + l += domainNameLen(rr.Mbox, off+l, compression, true) l += 4 // Serial l += 4 // Refresh l += 4 // Retry @@ -547,45 +547,45 @@ func (rr *SOA) len() int { l += 4 // Minttl return l } -func (rr *SPF) len() int { - l := rr.Hdr.len() +func (rr *SPF) len(off int, compression map[string]struct{}) int { + l := rr.Hdr.len(off, compression) for _, x := range rr.Txt { l += len(x) + 1 } return l } -func (rr *SRV) len() int { - l := rr.Hdr.len() +func (rr *SRV) len(off int, compression map[string]struct{}) int { + l := rr.Hdr.len(off, compression) l += 2 // Priority l += 2 // Weight l += 2 // Port - l += len(rr.Target) + 1 + l += domainNameLen(rr.Target, off+l, compression, false) return l } -func (rr *SSHFP) len() int { - l := rr.Hdr.len() +func (rr *SSHFP) len(off int, compression map[string]struct{}) int { + l := rr.Hdr.len(off, compression) l++ // Algorithm l++ // Type l += len(rr.FingerPrint)/2 + 1 return l } -func (rr *TA) len() int { - l := rr.Hdr.len() +func (rr *TA) len(off int, compression map[string]struct{}) int { + l := rr.Hdr.len(off, compression) l += 2 // KeyTag l++ // Algorithm l++ // DigestType l += len(rr.Digest)/2 + 1 return l } -func (rr *TALINK) len() int { - l := rr.Hdr.len() - l += len(rr.PreviousName) + 1 - l += len(rr.NextName) + 1 +func (rr *TALINK) len(off int, compression map[string]struct{}) int { + l := rr.Hdr.len(off, compression) + l += domainNameLen(rr.PreviousName, off+l, compression, false) + l += domainNameLen(rr.NextName, off+l, compression, false) return l } -func (rr *TKEY) len() int { - l := rr.Hdr.len() - l += len(rr.Algorithm) + 1 +func (rr *TKEY) len(off int, compression map[string]struct{}) int { + l := rr.Hdr.len(off, compression) + l += domainNameLen(rr.Algorithm, off+l, compression, false) l += 4 // Inception l += 4 // Expiration l += 2 // Mode @@ -596,17 +596,17 @@ func (rr *TKEY) len() int { l += len(rr.OtherData) / 2 return l } -func (rr *TLSA) len() int { - l := rr.Hdr.len() +func (rr *TLSA) len(off int, compression map[string]struct{}) int { + l := rr.Hdr.len(off, compression) l++ // Usage l++ // Selector l++ // MatchingType l += len(rr.Certificate)/2 + 1 return l } -func (rr *TSIG) len() int { - l := rr.Hdr.len() - l += len(rr.Algorithm) + 1 +func (rr *TSIG) len(off int, compression map[string]struct{}) int { + l := rr.Hdr.len(off, compression) + l += domainNameLen(rr.Algorithm, off+l, compression, false) l += 6 // TimeSigned l += 2 // Fudge l += 2 // MACSize @@ -617,32 +617,32 @@ func (rr *TSIG) len() int { l += len(rr.OtherData) / 2 return l } -func (rr *TXT) len() int { - l := rr.Hdr.len() +func (rr *TXT) len(off int, compression map[string]struct{}) int { + l := rr.Hdr.len(off, compression) for _, x := range rr.Txt { l += len(x) + 1 } return l } -func (rr *UID) len() int { - l := rr.Hdr.len() +func (rr *UID) len(off int, compression map[string]struct{}) int { + l := rr.Hdr.len(off, compression) l += 4 // Uid return l } -func (rr *UINFO) len() int { - l := rr.Hdr.len() +func (rr *UINFO) len(off int, compression map[string]struct{}) int { + l := rr.Hdr.len(off, compression) l += len(rr.Uinfo) + 1 return l } -func (rr *URI) len() int { - l := rr.Hdr.len() +func (rr *URI) len(off int, compression map[string]struct{}) int { + l := rr.Hdr.len(off, compression) l += 2 // Priority l += 2 // Weight l += len(rr.Target) return l } -func (rr *X25) len() int { - l := rr.Hdr.len() +func (rr *X25) len(off int, compression map[string]struct{}) int { + l := rr.Hdr.len(off, compression) l += len(rr.PSDNAddress) + 1 return l } diff --git a/vendor/github.com/pingcap/errors/LICENSE b/vendor/github.com/pingcap/errors/LICENSE new file mode 100644 index 000000000..835ba3e75 --- /dev/null +++ b/vendor/github.com/pingcap/errors/LICENSE @@ -0,0 +1,23 @@ +Copyright (c) 2015, Dave Cheney +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +* Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + +* 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 HOLDER 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/pingcap/errors/README.md b/vendor/github.com/pingcap/errors/README.md new file mode 100644 index 000000000..6483ba2af --- /dev/null +++ b/vendor/github.com/pingcap/errors/README.md @@ -0,0 +1,52 @@ +# errors [![Travis-CI](https://travis-ci.org/pkg/errors.svg)](https://travis-ci.org/pkg/errors) [![AppVeyor](https://ci.appveyor.com/api/projects/status/b98mptawhudj53ep/branch/master?svg=true)](https://ci.appveyor.com/project/davecheney/errors/branch/master) [![GoDoc](https://godoc.org/github.com/pkg/errors?status.svg)](http://godoc.org/github.com/pkg/errors) [![Report card](https://goreportcard.com/badge/github.com/pkg/errors)](https://goreportcard.com/report/github.com/pkg/errors) [![Sourcegraph](https://sourcegraph.com/github.com/pkg/errors/-/badge.svg)](https://sourcegraph.com/github.com/pkg/errors?badge) + +Package errors provides simple error handling primitives. + +`go get github.com/pkg/errors` + +The traditional error handling idiom in Go is roughly akin to +```go +if err != nil { + return err +} +``` +which applied recursively up the call stack results in error reports without context or debugging information. The errors package allows programmers to add context to the failure path in their code in a way that does not destroy the original value of the error. + +## Adding context to an error + +The errors.Wrap function returns a new error that adds context to the original error. For example +```go +_, err := ioutil.ReadAll(r) +if err != nil { + return errors.Wrap(err, "read failed") +} +``` +## Retrieving the cause of an error + +Using `errors.Wrap` constructs a stack of errors, adding context to the preceding error. Depending on the nature of the error it may be necessary to reverse the operation of errors.Wrap to retrieve the original error for inspection. Any error value which implements this interface can be inspected by `errors.Cause`. +```go +type causer interface { + Cause() error +} +``` +`errors.Cause` will recursively retrieve the topmost error which does not implement `causer`, which is assumed to be the original cause. For example: +```go +switch err := errors.Cause(err).(type) { +case *MyError: + // handle specifically +default: + // unknown error +} +``` + +[Read the package documentation for more information](https://godoc.org/github.com/pkg/errors). + +## Contributing + +We welcome pull requests, bug fixes and issue reports. With that said, the bar for adding new symbols to this package is intentionally set high. + +Before proposing a change, please discuss your change by raising an issue. + +## License + +BSD-2-Clause diff --git a/vendor/github.com/pingcap/errors/appveyor.yml b/vendor/github.com/pingcap/errors/appveyor.yml new file mode 100644 index 000000000..a932eade0 --- /dev/null +++ b/vendor/github.com/pingcap/errors/appveyor.yml @@ -0,0 +1,32 @@ +version: build-{build}.{branch} + +clone_folder: C:\gopath\src\github.com\pkg\errors +shallow_clone: true # for startup speed + +environment: + GOPATH: C:\gopath + +platform: + - x64 + +# http://www.appveyor.com/docs/installed-software +install: + # some helpful output for debugging builds + - go version + - go env + # pre-installed MinGW at C:\MinGW is 32bit only + # but MSYS2 at C:\msys64 has mingw64 + - set PATH=C:\msys64\mingw64\bin;%PATH% + - gcc --version + - g++ --version + +build_script: + - go install -v ./... + +test_script: + - set PATH=C:\gopath\bin;%PATH% + - go test -v ./... + +#artifacts: +# - path: '%GOPATH%\bin\*.exe' +deploy: off diff --git a/vendor/github.com/pingcap/errors/errors.go b/vendor/github.com/pingcap/errors/errors.go new file mode 100644 index 000000000..2e1d3f628 --- /dev/null +++ b/vendor/github.com/pingcap/errors/errors.go @@ -0,0 +1,324 @@ +// Package errors provides simple error handling primitives. +// +// The traditional error handling idiom in Go is roughly akin to +// +// if err != nil { +// return err +// } +// +// which applied recursively up the call stack results in error reports +// without context or debugging information. The errors package allows +// programmers to add context to the failure path in their code in a way +// that does not destroy the original value of the error. +// +// Adding context to an error +// +// The errors.Annotate function returns a new error that adds context to the +// original error by recording a stack trace at the point Annotate is called, +// and the supplied message. For example +// +// _, err := ioutil.ReadAll(r) +// if err != nil { +// return errors.Annotate(err, "read failed") +// } +// +// If additional control is required the errors.AddStack and errors.WithMessage +// functions destructure errors.Annotate into its component operations of annotating +// an error with a stack trace and an a message, respectively. +// +// Retrieving the cause of an error +// +// Using errors.Annotate constructs a stack of errors, adding context to the +// preceding error. Depending on the nature of the error it may be necessary +// to reverse the operation of errors.Annotate to retrieve the original error +// for inspection. Any error value which implements this interface +// +// type causer interface { +// Cause() error +// } +// +// can be inspected by errors.Cause. errors.Cause will recursively retrieve +// the topmost error which does not implement causer, which is assumed to be +// the original cause. For example: +// +// switch err := errors.Cause(err).(type) { +// case *MyError: +// // handle specifically +// default: +// // unknown error +// } +// +// causer interface is not exported by this package, but is considered a part +// of stable public API. +// errors.Unwrap is also available: this will retrieve the next error in the chain. +// +// Formatted printing of errors +// +// All error values returned from this package implement fmt.Formatter and can +// be formatted by the fmt package. The following verbs are supported +// +// %s print the error. If the error has a Cause it will be +// printed recursively +// %v see %s +// %+v extended format. Each Frame of the error's StackTrace will +// be printed in detail. +// +// Retrieving the stack trace of an error or wrapper +// +// New, Errorf, Annotate, and Annotatef record a stack trace at the point they are invoked. +// This information can be retrieved with the StackTracer interface that returns +// a StackTrace. Where errors.StackTrace is defined as +// +// type StackTrace []Frame +// +// The Frame type represents a call site in the stack trace. Frame supports +// the fmt.Formatter interface that can be used for printing information about +// the stack trace of this error. For example: +// +// if stacked := errors.GetStackTracer(err); stacked != nil { +// for _, f := range stacked.StackTrace() { +// fmt.Printf("%+s:%d", f) +// } +// } +// +// See the documentation for Frame.Format for more details. +// +// errors.Find can be used to search for an error in the error chain. +package errors + +import ( + "fmt" + "io" +) + +// New returns an error with the supplied message. +// New also records the stack trace at the point it was called. +func New(message string) error { + return &fundamental{ + msg: message, + stack: callers(), + } +} + +// Errorf formats according to a format specifier and returns the string +// as a value that satisfies error. +// Errorf also records the stack trace at the point it was called. +func Errorf(format string, args ...interface{}) error { + return &fundamental{ + msg: fmt.Sprintf(format, args...), + stack: callers(), + } +} + +// StackTraceAware is an optimization to avoid repetitive traversals of an error chain. +// HasStack checks for this marker first. +// Annotate/Wrap and Annotatef/Wrapf will produce this marker. +type StackTraceAware interface { + HasStack() bool +} + +// HasStack tells whether a StackTracer exists in the error chain +func HasStack(err error) bool { + if errWithStack, ok := err.(StackTraceAware); ok { + return errWithStack.HasStack() + } + return GetStackTracer(err) != nil +} + +// fundamental is an error that has a message and a stack, but no caller. +type fundamental struct { + msg string + *stack +} + +func (f *fundamental) Error() string { return f.msg } + +func (f *fundamental) Format(s fmt.State, verb rune) { + switch verb { + case 'v': + if s.Flag('+') { + io.WriteString(s, f.msg) + f.stack.Format(s, verb) + return + } + fallthrough + case 's': + io.WriteString(s, f.msg) + case 'q': + fmt.Fprintf(s, "%q", f.msg) + } +} + +// WithStack annotates err with a stack trace at the point WithStack was called. +// If err is nil, WithStack returns nil. +// +// For most use cases this is deprecated and AddStack should be used (which will ensure just one stack trace). +// However, one may want to use this in some situations, for example to create a 2nd trace across a goroutine. +func WithStack(err error) error { + if err == nil { + return nil + } + + return &withStack{ + err, + callers(), + } +} + +// AddStack is similar to WithStack. +// However, it will first check with HasStack to see if a stack trace already exists in the causer chain before creating another one. +func AddStack(err error) error { + if HasStack(err) { + return err + } + return WithStack(err) +} + +type withStack struct { + error + *stack +} + +func (w *withStack) Cause() error { return w.error } + +func (w *withStack) Format(s fmt.State, verb rune) { + switch verb { + case 'v': + if s.Flag('+') { + fmt.Fprintf(s, "%+v", w.Cause()) + w.stack.Format(s, verb) + return + } + fallthrough + case 's': + io.WriteString(s, w.Error()) + case 'q': + fmt.Fprintf(s, "%q", w.Error()) + } +} + +// Wrap returns an error annotating err with a stack trace +// at the point Wrap is called, and the supplied message. +// If err is nil, Wrap returns nil. +// +// For most use cases this is deprecated in favor of Annotate. +// Annotate avoids creating duplicate stack traces. +func Wrap(err error, message string) error { + if err == nil { + return nil + } + hasStack := HasStack(err) + err = &withMessage{ + cause: err, + msg: message, + causeHasStack: hasStack, + } + return &withStack{ + err, + callers(), + } +} + +// Wrapf returns an error annotating err with a stack trace +// at the point Wrapf is call, and the format specifier. +// If err is nil, Wrapf returns nil. +// +// For most use cases this is deprecated in favor of Annotatef. +// Annotatef avoids creating duplicate stack traces. +func Wrapf(err error, format string, args ...interface{}) error { + if err == nil { + return nil + } + hasStack := HasStack(err) + err = &withMessage{ + cause: err, + msg: fmt.Sprintf(format, args...), + causeHasStack: hasStack, + } + return &withStack{ + err, + callers(), + } +} + +// WithMessage annotates err with a new message. +// If err is nil, WithMessage returns nil. +func WithMessage(err error, message string) error { + if err == nil { + return nil + } + return &withMessage{ + cause: err, + msg: message, + causeHasStack: HasStack(err), + } +} + +type withMessage struct { + cause error + msg string + causeHasStack bool +} + +func (w *withMessage) Error() string { return w.msg + ": " + w.cause.Error() } +func (w *withMessage) Cause() error { return w.cause } +func (w *withMessage) HasStack() bool { return w.causeHasStack } + +func (w *withMessage) Format(s fmt.State, verb rune) { + switch verb { + case 'v': + if s.Flag('+') { + fmt.Fprintf(s, "%+v\n", w.Cause()) + io.WriteString(s, w.msg) + return + } + fallthrough + case 's', 'q': + io.WriteString(s, w.Error()) + } +} + +// Cause returns the underlying cause of the error, if possible. +// An error value has a cause if it implements the following +// interface: +// +// type causer interface { +// Cause() error +// } +// +// If the error does not implement Cause, the original error will +// be returned. If the error is nil, nil will be returned without further +// investigation. +func Cause(err error) error { + cause := Unwrap(err) + if cause == nil { + return err + } + return Cause(cause) +} + +// Unwrap uses causer to return the next error in the chain or nil. +// This goes one-level deeper, whereas Cause goes as far as possible +func Unwrap(err error) error { + type causer interface { + Cause() error + } + if unErr, ok := err.(causer); ok { + return unErr.Cause() + } + return nil +} + +// Find an error in the chain that matches a test function. +// returns nil if no error is found. +func Find(origErr error, test func(error) bool) error { + var foundErr error + WalkDeep(origErr, func(err error) bool { + if test(err) { + foundErr = err + return true + } + return false + }) + return foundErr +} diff --git a/vendor/github.com/pingcap/errors/group.go b/vendor/github.com/pingcap/errors/group.go new file mode 100644 index 000000000..e5a969ab7 --- /dev/null +++ b/vendor/github.com/pingcap/errors/group.go @@ -0,0 +1,42 @@ +package errors + +// ErrorGroup is an interface for multiple errors that are not a chain. +// This happens for example when executing multiple operations in parallel. +type ErrorGroup interface { + Errors() []error +} + +// Errors uses the ErrorGroup interface to return a slice of errors. +// If the ErrorGroup interface is not implemented it returns an array containing just the given error. +func Errors(err error) []error { + if eg, ok := err.(ErrorGroup); ok { + return eg.Errors() + } + return []error{err} +} + +// WalkDeep does a depth-first traversal of all errors. +// Any ErrorGroup is traversed (after going deep). +// The visitor function can return true to end the traversal early +// In that case, WalkDeep will return true, otherwise false. +func WalkDeep(err error, visitor func(err error) bool) bool { + // Go deep + unErr := err + for unErr != nil { + if done := visitor(unErr); done { + return true + } + unErr = Unwrap(unErr) + } + + // Go wide + if group, ok := err.(ErrorGroup); ok { + for _, err := range group.Errors() { + if early := WalkDeep(err, visitor); early { + return true + } + } + } + + return false +} diff --git a/vendor/github.com/pingcap/errors/juju_adaptor.go b/vendor/github.com/pingcap/errors/juju_adaptor.go new file mode 100644 index 000000000..8bcfe2f37 --- /dev/null +++ b/vendor/github.com/pingcap/errors/juju_adaptor.go @@ -0,0 +1,100 @@ +package errors + +import ( + "fmt" + "strings" +) + +// ==================== juju adaptor start ======================== + +// Trace just calls AddStack. +func Trace(err error) error { + return AddStack(err) +} + +// Annotate adds a message and ensures there is a stack trace. +func Annotate(err error, message string) error { + if err == nil { + return nil + } + hasStack := HasStack(err) + err = &withMessage{ + cause: err, + msg: message, + causeHasStack: hasStack, + } + if hasStack { + return err + } + return &withStack{ + err, + callers(), + } +} + +// Annotatef adds a message and ensures there is a stack trace. +func Annotatef(err error, format string, args ...interface{}) error { + if err == nil { + return nil + } + hasStack := HasStack(err) + err = &withMessage{ + cause: err, + msg: fmt.Sprintf(format, args...), + causeHasStack: hasStack, + } + if hasStack { + return err + } + return &withStack{ + err, + callers(), + } +} + +// ErrorStack will format a stack trace if it is available, otherwise it will be Error() +// If the error is nil, the empty string is returned +// Note that this just calls fmt.Sprintf("%+v", err) +func ErrorStack(err error) string { + if err == nil { + return "" + } + return fmt.Sprintf("%+v", err) +} + +// IsNotFound reports whether err was not found error. +func IsNotFound(err error) bool { + return strings.Contains(err.Error(), "not found") +} + +// NotFoundf represents an error with not found message. +func NotFoundf(format string, args ...interface{}) error { + return Errorf(format+" not found", args...) +} + +// BadRequestf represents an error with bad request message. +func BadRequestf(format string, args ...interface{}) error { + return Errorf(format+" bad request", args...) +} + +// NotSupportedf represents an error with not supported message. +func NotSupportedf(format string, args ...interface{}) error { + return Errorf(format+" not supported", args...) +} + +// NotValidf represents an error with not valid message. +func NotValidf(format string, args ...interface{}) error { + return Errorf(format+" not valid", args...) +} + +// IsAlreadyExists reports whether err was already exists error. +func IsAlreadyExists(err error) bool { + return strings.Contains(err.Error(), "already exists") +} + +// AlreadyExistsf represents an error with already exists message. +func AlreadyExistsf(format string, args ...interface{}) error { + return Errorf(format+" already exists", args...) +} + +// ==================== juju adaptor end ======================== diff --git a/vendor/github.com/pingcap/errors/stack.go b/vendor/github.com/pingcap/errors/stack.go new file mode 100644 index 000000000..bb1e6a84f --- /dev/null +++ b/vendor/github.com/pingcap/errors/stack.go @@ -0,0 +1,226 @@ +package errors + +import ( + "bytes" + "fmt" + "io" + "path" + "runtime" + "strconv" + "strings" +) + +// StackTracer retrieves the StackTrace +// Generally you would want to use the GetStackTracer function to do that. +type StackTracer interface { + StackTrace() StackTrace +} + +// GetStackTracer will return the first StackTracer in the causer chain. +// This function is used by AddStack to avoid creating redundant stack traces. +// +// You can also use the StackTracer interface on the returned error to get the stack trace. +func GetStackTracer(origErr error) StackTracer { + var stacked StackTracer + WalkDeep(origErr, func(err error) bool { + if stackTracer, ok := err.(StackTracer); ok { + stacked = stackTracer + return true + } + return false + }) + return stacked +} + +// Frame represents a program counter inside a stack frame. +type Frame uintptr + +// pc returns the program counter for this frame; +// multiple frames may have the same PC value. +func (f Frame) pc() uintptr { return uintptr(f) - 1 } + +// file returns the full path to the file that contains the +// function for this Frame's pc. +func (f Frame) file() string { + fn := runtime.FuncForPC(f.pc()) + if fn == nil { + return "unknown" + } + file, _ := fn.FileLine(f.pc()) + return file +} + +// line returns the line number of source code of the +// function for this Frame's pc. +func (f Frame) line() int { + fn := runtime.FuncForPC(f.pc()) + if fn == nil { + return 0 + } + _, line := fn.FileLine(f.pc()) + return line +} + +// Format formats the frame according to the fmt.Formatter interface. +// +// %s source file +// %d source line +// %n function name +// %v equivalent to %s:%d +// +// Format accepts flags that alter the printing of some verbs, as follows: +// +// %+s function name and path of source file relative to the compile time +// GOPATH separated by \n\t (\n\t) +// %+v equivalent to %+s:%d +func (f Frame) Format(s fmt.State, verb rune) { + f.format(s, s, verb) +} + +// format allows stack trace printing calls to be made with a bytes.Buffer. +func (f Frame) format(w io.Writer, s fmt.State, verb rune) { + switch verb { + case 's': + switch { + case s.Flag('+'): + pc := f.pc() + fn := runtime.FuncForPC(pc) + if fn == nil { + io.WriteString(w, "unknown") + } else { + file, _ := fn.FileLine(pc) + io.WriteString(w, fn.Name()) + io.WriteString(w, "\n\t") + io.WriteString(w, file) + } + default: + io.WriteString(w, path.Base(f.file())) + } + case 'd': + io.WriteString(w, strconv.Itoa(f.line())) + case 'n': + name := runtime.FuncForPC(f.pc()).Name() + io.WriteString(w, funcname(name)) + case 'v': + f.format(w, s, 's') + io.WriteString(w, ":") + f.format(w, s, 'd') + } +} + +// StackTrace is stack of Frames from innermost (newest) to outermost (oldest). +type StackTrace []Frame + +// Format formats the stack of Frames according to the fmt.Formatter interface. +// +// %s lists source files for each Frame in the stack +// %v lists the source file and line number for each Frame in the stack +// +// Format accepts flags that alter the printing of some verbs, as follows: +// +// %+v Prints filename, function, and line number for each Frame in the stack. +func (st StackTrace) Format(s fmt.State, verb rune) { + var b bytes.Buffer + switch verb { + case 'v': + switch { + case s.Flag('+'): + b.Grow(len(st) * stackMinLen) + for _, fr := range st { + b.WriteByte('\n') + fr.format(&b, s, verb) + } + case s.Flag('#'): + fmt.Fprintf(&b, "%#v", []Frame(st)) + default: + st.formatSlice(&b, s, verb) + } + case 's': + st.formatSlice(&b, s, verb) + } + io.Copy(s, &b) +} + +// formatSlice will format this StackTrace into the given buffer as a slice of +// Frame, only valid when called with '%s' or '%v'. +func (st StackTrace) formatSlice(b *bytes.Buffer, s fmt.State, verb rune) { + b.WriteByte('[') + if len(st) == 0 { + b.WriteByte(']') + return + } + + b.Grow(len(st) * (stackMinLen / 4)) + st[0].format(b, s, verb) + for _, fr := range st[1:] { + b.WriteByte(' ') + fr.format(b, s, verb) + } + b.WriteByte(']') +} + +// stackMinLen is a best-guess at the minimum length of a stack trace. It +// doesn't need to be exact, just give a good enough head start for the buffer +// to avoid the expensive early growth. +const stackMinLen = 96 + +// stack represents a stack of program counters. +type stack []uintptr + +func (s *stack) Format(st fmt.State, verb rune) { + switch verb { + case 'v': + switch { + case st.Flag('+'): + var b bytes.Buffer + b.Grow(len(*s) * stackMinLen) + for _, pc := range *s { + f := Frame(pc) + b.WriteByte('\n') + f.format(&b, st, 'v') + } + io.Copy(st, &b) + } + } +} + +func (s *stack) StackTrace() StackTrace { + f := make([]Frame, len(*s)) + for i := 0; i < len(f); i++ { + f[i] = Frame((*s)[i]) + } + return f +} + +func callers() *stack { + return callersSkip(4) +} + +func callersSkip(skip int) *stack { + const depth = 32 + var pcs [depth]uintptr + n := runtime.Callers(skip, pcs[:]) + var st stack = pcs[0:n] + return &st +} + +// funcname removes the path prefix component of a function's name reported by func.Name(). +func funcname(name string) string { + i := strings.LastIndex(name, "/") + name = name[i+1:] + i = strings.Index(name, ".") + return name[i+1:] +} + +// NewStack is for library implementers that want to generate a stack trace. +// Normally you should insted use AddStack to get an error with a stack trace. +// +// The result of this function can be turned into a stack trace by calling .StackTrace() +// +// This function takes an argument for the number of stack frames to skip. +// This avoids putting stack generation function calls like this one in the stack trace. +// A value of 0 will give you the line that called NewStack(0) +// A library author wrapping this in their own function will want to use a value of at least 1. +func NewStack(skip int) StackTracer { + return callersSkip(skip + 3) +} diff --git a/vendor/github.com/prometheus/procfs/Makefile b/vendor/github.com/prometheus/procfs/Makefile index 4d1098394..40540b02e 100644 --- a/vendor/github.com/prometheus/procfs/Makefile +++ b/vendor/github.com/prometheus/procfs/Makefile @@ -11,49 +11,7 @@ # See the License for the specific language governing permissions and # limitations under the License. -# Ensure GOBIN is not set during build so that promu is installed to the correct path -unexport GOBIN - -GO ?= go -GOFMT ?= $(GO)fmt -FIRST_GOPATH := $(firstword $(subst :, ,$(shell $(GO) env GOPATH))) -STATICCHECK := $(FIRST_GOPATH)/bin/staticcheck -pkgs = $(shell $(GO) list ./... | grep -v /vendor/) - -PREFIX ?= $(shell pwd) -BIN_DIR ?= $(shell pwd) - -ifdef DEBUG - bindata_flags = -debug -endif - -STATICCHECK_IGNORE = - -all: format staticcheck build test - -style: - @echo ">> checking code style" - @! $(GOFMT) -d $(shell find . -path ./vendor -prune -o -name '*.go' -print) | grep '^' - -check_license: - @echo ">> checking license header" - @./scripts/check_license.sh - -test: fixtures/.unpacked sysfs/fixtures/.unpacked - @echo ">> running all tests" - @$(GO) test -race $(shell $(GO) list ./... | grep -v /vendor/ | grep -v examples) - -format: - @echo ">> formatting code" - @$(GO) fmt $(pkgs) - -vet: - @echo ">> vetting code" - @$(GO) vet $(pkgs) - -staticcheck: $(STATICCHECK) - @echo ">> running staticcheck" - @$(STATICCHECK) -ignore "$(STATICCHECK_IGNORE)" $(pkgs) +include Makefile.common %/.unpacked: %.ttar ./ttar -C $(dir $*) -x -f $*.ttar @@ -65,13 +23,5 @@ update_fixtures: fixtures.ttar sysfs/fixtures.ttar rm -v $(dir $*)fixtures/.unpacked ./ttar -C $(dir $*) -c -f $*fixtures.ttar fixtures/ -$(FIRST_GOPATH)/bin/staticcheck: - @GOOS= GOARCH= $(GO) get -u honnef.co/go/tools/cmd/staticcheck - -.PHONY: all style check_license format test vet staticcheck - -# Declaring the binaries at their default locations as PHONY targets is a hack -# to ensure the latest version is downloaded on every make execution. -# If this is not desired, copy/symlink these binaries to a different path and -# set the respective environment variables. -.PHONY: $(GOPATH)/bin/staticcheck +.PHONY: test +test: fixtures/.unpacked sysfs/fixtures/.unpacked common-test diff --git a/vendor/github.com/prometheus/procfs/Makefile.common b/vendor/github.com/prometheus/procfs/Makefile.common new file mode 100644 index 000000000..741579e60 --- /dev/null +++ b/vendor/github.com/prometheus/procfs/Makefile.common @@ -0,0 +1,223 @@ +# Copyright 2018 The Prometheus Authors +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + + +# A common Makefile that includes rules to be reused in different prometheus projects. +# !!! Open PRs only against the prometheus/prometheus/Makefile.common repository! + +# Example usage : +# Create the main Makefile in the root project directory. +# include Makefile.common +# customTarget: +# @echo ">> Running customTarget" +# + +# Ensure GOBIN is not set during build so that promu is installed to the correct path +unexport GOBIN + +GO ?= go +GOFMT ?= $(GO)fmt +FIRST_GOPATH := $(firstword $(subst :, ,$(shell $(GO) env GOPATH))) +GOOPTS ?= + +GO_VERSION ?= $(shell $(GO) version) +GO_VERSION_NUMBER ?= $(word 3, $(GO_VERSION)) +PRE_GO_111 ?= $(shell echo $(GO_VERSION_NUMBER) | grep -E 'go1\.(10|[0-9])\.') + +unexport GOVENDOR +ifeq (, $(PRE_GO_111)) + ifneq (,$(wildcard go.mod)) + # Enforce Go modules support just in case the directory is inside GOPATH (and for Travis CI). + GO111MODULE := on + + ifneq (,$(wildcard vendor)) + # Always use the local vendor/ directory to satisfy the dependencies. + GOOPTS := $(GOOPTS) -mod=vendor + endif + endif +else + ifneq (,$(wildcard go.mod)) + ifneq (,$(wildcard vendor)) +$(warning This repository requires Go >= 1.11 because of Go modules) +$(warning Some recipes may not work as expected as the current Go runtime is '$(GO_VERSION_NUMBER)') + endif + else + # This repository isn't using Go modules (yet). + GOVENDOR := $(FIRST_GOPATH)/bin/govendor + endif + + unexport GO111MODULE +endif +PROMU := $(FIRST_GOPATH)/bin/promu +STATICCHECK := $(FIRST_GOPATH)/bin/staticcheck +pkgs = ./... + +GO_VERSION ?= $(shell $(GO) version) +GO_BUILD_PLATFORM ?= $(subst /,-,$(lastword $(GO_VERSION))) + +PROMU_VERSION ?= 0.2.0 +PROMU_URL := https://github.com/prometheus/promu/releases/download/v$(PROMU_VERSION)/promu-$(PROMU_VERSION).$(GO_BUILD_PLATFORM).tar.gz + +PREFIX ?= $(shell pwd) +BIN_DIR ?= $(shell pwd) +DOCKER_IMAGE_TAG ?= $(subst /,-,$(shell git rev-parse --abbrev-ref HEAD)) +DOCKER_REPO ?= prom + +.PHONY: all +all: precheck style staticcheck unused build test + +# This rule is used to forward a target like "build" to "common-build". This +# allows a new "build" target to be defined in a Makefile which includes this +# one and override "common-build" without override warnings. +%: common-% ; + +.PHONY: common-style +common-style: + @echo ">> checking code style" + @fmtRes=$$($(GOFMT) -d $$(find . -path ./vendor -prune -o -name '*.go' -print)); \ + if [ -n "$${fmtRes}" ]; then \ + echo "gofmt checking failed!"; echo "$${fmtRes}"; echo; \ + echo "Please ensure you are using $$($(GO) version) for formatting code."; \ + exit 1; \ + fi + +.PHONY: common-check_license +common-check_license: + @echo ">> checking license header" + @licRes=$$(for file in $$(find . -type f -iname '*.go' ! -path './vendor/*') ; do \ + awk 'NR<=3' $$file | grep -Eq "(Copyright|generated|GENERATED)" || echo $$file; \ + done); \ + if [ -n "$${licRes}" ]; then \ + echo "license header checking failed:"; echo "$${licRes}"; \ + exit 1; \ + fi + +.PHONY: common-test-short +common-test-short: + @echo ">> running short tests" + GO111MODULE=$(GO111MODULE) $(GO) test -short $(GOOPTS) $(pkgs) + +.PHONY: common-test +common-test: + @echo ">> running all tests" + GO111MODULE=$(GO111MODULE) $(GO) test -race $(GOOPTS) $(pkgs) + +.PHONY: common-format +common-format: + @echo ">> formatting code" + GO111MODULE=$(GO111MODULE) $(GO) fmt $(GOOPTS) $(pkgs) + +.PHONY: common-vet +common-vet: + @echo ">> vetting code" + GO111MODULE=$(GO111MODULE) $(GO) vet $(GOOPTS) $(pkgs) + +.PHONY: common-staticcheck +common-staticcheck: $(STATICCHECK) + @echo ">> running staticcheck" +ifdef GO111MODULE + GO111MODULE=$(GO111MODULE) $(STATICCHECK) -ignore "$(STATICCHECK_IGNORE)" -checks "SA*" $(pkgs) +else + $(STATICCHECK) -ignore "$(STATICCHECK_IGNORE)" $(pkgs) +endif + +.PHONY: common-unused +common-unused: $(GOVENDOR) +ifdef GOVENDOR + @echo ">> running check for unused packages" + @$(GOVENDOR) list +unused | grep . && exit 1 || echo 'No unused packages' +else +ifdef GO111MODULE + @echo ">> running check for unused/missing packages in go.mod" + GO111MODULE=$(GO111MODULE) $(GO) mod tidy + @git diff --exit-code -- go.sum go.mod +ifneq (,$(wildcard vendor)) + @echo ">> running check for unused packages in vendor/" + GO111MODULE=$(GO111MODULE) $(GO) mod vendor + @git diff --exit-code -- go.sum go.mod vendor/ +endif +endif +endif + +.PHONY: common-build +common-build: promu + @echo ">> building binaries" + GO111MODULE=$(GO111MODULE) $(PROMU) build --prefix $(PREFIX) + +.PHONY: common-tarball +common-tarball: promu + @echo ">> building release tarball" + $(PROMU) tarball --prefix $(PREFIX) $(BIN_DIR) + +.PHONY: common-docker +common-docker: + docker build -t "$(DOCKER_REPO)/$(DOCKER_IMAGE_NAME):$(DOCKER_IMAGE_TAG)" . + +.PHONY: common-docker-publish +common-docker-publish: + docker push "$(DOCKER_REPO)/$(DOCKER_IMAGE_NAME)" + +.PHONY: common-docker-tag-latest +common-docker-tag-latest: + docker tag "$(DOCKER_REPO)/$(DOCKER_IMAGE_NAME):$(DOCKER_IMAGE_TAG)" "$(DOCKER_REPO)/$(DOCKER_IMAGE_NAME):latest" + +.PHONY: promu +promu: $(PROMU) + +$(PROMU): + curl -s -L $(PROMU_URL) | tar -xvz -C /tmp + mkdir -v -p $(FIRST_GOPATH)/bin + cp -v /tmp/promu-$(PROMU_VERSION).$(GO_BUILD_PLATFORM)/promu $(PROMU) + +.PHONY: proto +proto: + @echo ">> generating code from proto files" + @./scripts/genproto.sh + +.PHONY: $(STATICCHECK) +$(STATICCHECK): +ifdef GO111MODULE +# Get staticcheck from a temporary directory to avoid modifying the local go.{mod,sum}. +# See https://github.com/golang/go/issues/27643. +# For now, we are using the next branch of staticcheck because master isn't compatible yet with Go modules. + tmpModule=$$(mktemp -d 2>&1) && \ + mkdir -p $${tmpModule}/staticcheck && \ + cd "$${tmpModule}"/staticcheck && \ + GO111MODULE=on $(GO) mod init example.com/staticcheck && \ + GO111MODULE=on GOOS= GOARCH= $(GO) get -u honnef.co/go/tools/cmd/staticcheck@next && \ + rm -rf $${tmpModule}; +else + GOOS= GOARCH= GO111MODULE=off $(GO) get -u honnef.co/go/tools/cmd/staticcheck +endif + +ifdef GOVENDOR +.PHONY: $(GOVENDOR) +$(GOVENDOR): + GOOS= GOARCH= $(GO) get -u github.com/kardianos/govendor +endif + +.PHONY: precheck +precheck:: + +define PRECHECK_COMMAND_template = +precheck:: $(1)_precheck + + +PRECHECK_COMMAND_$(1) ?= $(1) $$(strip $$(PRECHECK_OPTIONS_$(1))) +.PHONY: $(1)_precheck +$(1)_precheck: + @if ! $$(PRECHECK_COMMAND_$(1)) 1>/dev/null 2>&1; then \ + echo "Execution of '$$(PRECHECK_COMMAND_$(1))' command failed. Is $(1) installed?"; \ + exit 1; \ + fi +endef diff --git a/vendor/github.com/prometheus/procfs/go.mod b/vendor/github.com/prometheus/procfs/go.mod new file mode 100644 index 000000000..e89ee6c90 --- /dev/null +++ b/vendor/github.com/prometheus/procfs/go.mod @@ -0,0 +1 @@ +module github.com/prometheus/procfs diff --git a/vendor/github.com/syndtr/goleveldb/leveldb/comparer/comparer.go b/vendor/github.com/syndtr/goleveldb/leveldb/comparer/comparer.go index 14a28f16f..2c522db23 100644 --- a/vendor/github.com/syndtr/goleveldb/leveldb/comparer/comparer.go +++ b/vendor/github.com/syndtr/goleveldb/leveldb/comparer/comparer.go @@ -36,7 +36,7 @@ type Comparer interface { // by any users of this package. Name() string - // Bellow are advanced functions used used to reduce the space requirements + // Bellow are advanced functions used to reduce the space requirements // for internal data structures such as index blocks. // Separator appends a sequence of bytes x to dst such that a <= x && x < b, diff --git a/vendor/github.com/syndtr/goleveldb/leveldb/db_compaction.go b/vendor/github.com/syndtr/goleveldb/leveldb/db_compaction.go index 28e50906a..0c1b9a53b 100644 --- a/vendor/github.com/syndtr/goleveldb/leveldb/db_compaction.go +++ b/vendor/github.com/syndtr/goleveldb/leveldb/db_compaction.go @@ -663,7 +663,7 @@ type cCmd interface { } type cAuto struct { - // Note for table compaction, an empty ackC represents it's a compaction waiting command. + // Note for table compaction, an non-empty ackC represents it's a compaction waiting command. ackC chan<- error } @@ -777,8 +777,8 @@ func (db *DB) mCompaction() { func (db *DB) tCompaction() { var ( - x cCmd - ackQ, waitQ []cCmd + x cCmd + waitQ []cCmd ) defer func() { @@ -787,10 +787,6 @@ func (db *DB) tCompaction() { panic(x) } } - for i := range ackQ { - ackQ[i].ack(ErrClosed) - ackQ[i] = nil - } for i := range waitQ { waitQ[i].ack(ErrClosed) waitQ[i] = nil @@ -821,11 +817,6 @@ func (db *DB) tCompaction() { waitQ = waitQ[:0] } } else { - for i := range ackQ { - ackQ[i].ack(nil) - ackQ[i] = nil - } - ackQ = ackQ[:0] for i := range waitQ { waitQ[i].ack(nil) waitQ[i] = nil @@ -844,9 +835,12 @@ func (db *DB) tCompaction() { switch cmd := x.(type) { case cAuto: if cmd.ackC != nil { - waitQ = append(waitQ, x) - } else { - ackQ = append(ackQ, x) + // Check the write pause state before caching it. + if db.resumeWrite() { + x.ack(nil) + } else { + waitQ = append(waitQ, x) + } } case cRange: x.ack(db.tableRangeCompaction(cmd.level, cmd.min, cmd.max)) diff --git a/vendor/golang.org/x/sys/unix/mkall.sh b/vendor/golang.org/x/sys/unix/mkall.sh index 9b76ad669..4f92537ca 100755 --- a/vendor/golang.org/x/sys/unix/mkall.sh +++ b/vendor/golang.org/x/sys/unix/mkall.sh @@ -10,7 +10,7 @@ GOOSARCH="${GOOS}_${GOARCH}" # defaults -mksyscall="./mksyscall.pl" +mksyscall="go run mksyscall.go" mkerrors="./mkerrors.sh" zerrors="zerrors_$GOOSARCH.go" mksysctl="" @@ -71,7 +71,7 @@ aix_ppc64) ;; darwin_386) mkerrors="$mkerrors -m32" - mksyscall="./mksyscall.pl -l32" + mksyscall="go run mksyscall.go -l32" mksysnum="./mksysnum_darwin.pl $(xcrun --show-sdk-path --sdk macosx)/usr/include/sys/syscall.h" mktypes="GOARCH=$GOARCH go tool cgo -godefs" ;; @@ -92,13 +92,13 @@ darwin_arm64) ;; dragonfly_amd64) mkerrors="$mkerrors -m64" - mksyscall="./mksyscall.pl -dragonfly" + mksyscall="go run mksyscall.go -dragonfly" mksysnum="curl -s 'http://gitweb.dragonflybsd.org/dragonfly.git/blob_plain/HEAD:/sys/kern/syscalls.master' | ./mksysnum_dragonfly.pl" mktypes="GOARCH=$GOARCH go tool cgo -godefs" ;; freebsd_386) mkerrors="$mkerrors -m32" - mksyscall="./mksyscall.pl -l32" + mksyscall="go run mksyscall.go -l32" mksysnum="curl -s 'http://svn.freebsd.org/base/stable/10/sys/kern/syscalls.master' | ./mksysnum_freebsd.pl" mktypes="GOARCH=$GOARCH go tool cgo -godefs" ;; @@ -109,7 +109,7 @@ freebsd_amd64) ;; freebsd_arm) mkerrors="$mkerrors" - mksyscall="./mksyscall.pl -l32 -arm" + mksyscall="go run mksyscall.go -l32 -arm" mksysnum="curl -s 'http://svn.freebsd.org/base/stable/10/sys/kern/syscalls.master' | ./mksysnum_freebsd.pl" # Let the type of C char be signed for making the bare syscall # API consistent across platforms. @@ -124,19 +124,19 @@ linux_sparc64) ;; netbsd_386) mkerrors="$mkerrors -m32" - mksyscall="./mksyscall.pl -l32 -netbsd" + mksyscall="go run mksyscall.go -l32 -netbsd" mksysnum="curl -s 'http://cvsweb.netbsd.org/bsdweb.cgi/~checkout~/src/sys/kern/syscalls.master' | ./mksysnum_netbsd.pl" mktypes="GOARCH=$GOARCH go tool cgo -godefs" ;; netbsd_amd64) mkerrors="$mkerrors -m64" - mksyscall="./mksyscall.pl -netbsd" + mksyscall="go run mksyscall.go -netbsd" mksysnum="curl -s 'http://cvsweb.netbsd.org/bsdweb.cgi/~checkout~/src/sys/kern/syscalls.master' | ./mksysnum_netbsd.pl" mktypes="GOARCH=$GOARCH go tool cgo -godefs" ;; netbsd_arm) mkerrors="$mkerrors" - mksyscall="./mksyscall.pl -l32 -netbsd -arm" + mksyscall="go run mksyscall.go -l32 -netbsd -arm" mksysnum="curl -s 'http://cvsweb.netbsd.org/bsdweb.cgi/~checkout~/src/sys/kern/syscalls.master' | ./mksysnum_netbsd.pl" # Let the type of C char be signed for making the bare syscall # API consistent across platforms. @@ -144,21 +144,21 @@ netbsd_arm) ;; openbsd_386) mkerrors="$mkerrors -m32" - mksyscall="./mksyscall.pl -l32 -openbsd" + mksyscall="go run mksyscall.go -l32 -openbsd" mksysctl="./mksysctl_openbsd.pl" mksysnum="curl -s 'http://cvsweb.openbsd.org/cgi-bin/cvsweb/~checkout~/src/sys/kern/syscalls.master' | ./mksysnum_openbsd.pl" mktypes="GOARCH=$GOARCH go tool cgo -godefs" ;; openbsd_amd64) mkerrors="$mkerrors -m64" - mksyscall="./mksyscall.pl -openbsd" + mksyscall="go run mksyscall.go -openbsd" mksysctl="./mksysctl_openbsd.pl" mksysnum="curl -s 'http://cvsweb.openbsd.org/cgi-bin/cvsweb/~checkout~/src/sys/kern/syscalls.master' | ./mksysnum_openbsd.pl" mktypes="GOARCH=$GOARCH go tool cgo -godefs" ;; openbsd_arm) mkerrors="$mkerrors" - mksyscall="./mksyscall.pl -l32 -openbsd -arm" + mksyscall="go run mksyscall.go -l32 -openbsd -arm" mksysctl="./mksysctl_openbsd.pl" mksysnum="curl -s 'http://cvsweb.openbsd.org/cgi-bin/cvsweb/~checkout~/src/sys/kern/syscalls.master' | ./mksysnum_openbsd.pl" # Let the type of C char be signed for making the bare syscall diff --git a/vendor/golang.org/x/sys/unix/mksyscall.go b/vendor/golang.org/x/sys/unix/mksyscall.go new file mode 100644 index 000000000..36c0b5ac4 --- /dev/null +++ b/vendor/golang.org/x/sys/unix/mksyscall.go @@ -0,0 +1,367 @@ +// Copyright 2018 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. + +// +build ignore + +/* +This program reads a file containing function prototypes +(like syscall_darwin.go) and generates system call bodies. +The prototypes are marked by lines beginning with "//sys" +and read like func declarations if //sys is replaced by func, but: + * The parameter lists must give a name for each argument. + This includes return parameters. + * The parameter lists must give a type for each argument: + the (x, y, z int) shorthand is not allowed. + * If the return parameter is an error number, it must be named errno. + +A line beginning with //sysnb is like //sys, except that the +goroutine will not be suspended during the execution of the system +call. This must only be used for system calls which can never +block, as otherwise the system call could cause all goroutines to +hang. +*/ +package main + +import ( + "bufio" + "flag" + "fmt" + "os" + "regexp" + "strings" +) + +var ( + b32 = flag.Bool("b32", false, "32bit big-endian") + l32 = flag.Bool("l32", false, "32bit little-endian") + plan9 = flag.Bool("plan9", false, "plan9") + openbsd = flag.Bool("openbsd", false, "openbsd") + netbsd = flag.Bool("netbsd", false, "netbsd") + dragonfly = flag.Bool("dragonfly", false, "dragonfly") + arm = flag.Bool("arm", false, "arm") // 64-bit value should use (even, odd)-pair + tags = flag.String("tags", "", "build tags") + filename = flag.String("output", "", "output file name (standard output if omitted)") +) + +// cmdLine returns this programs's commandline arguments +func cmdLine() string { + return "go run mksyscall.go " + strings.Join(os.Args[1:], " ") +} + +// buildTags returns build tags +func buildTags() string { + return *tags +} + +// Param is function parameter +type Param struct { + Name string + Type string +} + +// usage prints the program usage +func usage() { + fmt.Fprintf(os.Stderr, "usage: go run mksyscall.go [-b32 | -l32] [-tags x,y] [file ...]\n") + os.Exit(1) +} + +// parseParamList parses parameter list and returns a slice of parameters +func parseParamList(list string) []string { + list = strings.TrimSpace(list) + if list == "" { + return []string{} + } + return regexp.MustCompile(`\s*,\s*`).Split(list, -1) +} + +// parseParam splits a parameter into name and type +func parseParam(p string) Param { + ps := regexp.MustCompile(`^(\S*) (\S*)$`).FindStringSubmatch(p) + if ps == nil { + fmt.Fprintf(os.Stderr, "malformed parameter: %s\n", p) + os.Exit(1) + } + return Param{ps[1], ps[2]} +} + +func main() { + // Get the OS and architecture (using GOARCH_TARGET if it exists) + goos := os.Getenv("GOOS") + goarch := os.Getenv("GOARCH_TARGET") + if goarch == "" { + goarch = os.Getenv("GOARCH") + } + + // Check that we are using the new build system if we should + if goos == "linux" && goarch != "sparc64" { + if os.Getenv("GOLANG_SYS_BUILD") != "docker" { + fmt.Fprintf(os.Stderr, "In the new build system, mksyscall should not be called directly.\n") + fmt.Fprintf(os.Stderr, "See README.md\n") + os.Exit(1) + } + } + + flag.Usage = usage + flag.Parse() + if len(flag.Args()) <= 0 { + fmt.Fprintf(os.Stderr, "no files to parse provided\n") + usage() + } + + endianness := "" + if *b32 { + endianness = "big-endian" + } else if *l32 { + endianness = "little-endian" + } + + text := "" + for _, path := range flag.Args() { + file, err := os.Open(path) + if err != nil { + fmt.Fprintf(os.Stderr, err.Error()) + os.Exit(1) + } + s := bufio.NewScanner(file) + for s.Scan() { + t := s.Text() + t = strings.TrimSpace(t) + t = regexp.MustCompile(`\s+`).ReplaceAllString(t, ` `) + nonblock := regexp.MustCompile(`^\/\/sysnb `).FindStringSubmatch(t) + if regexp.MustCompile(`^\/\/sys `).FindStringSubmatch(t) == nil && nonblock == nil { + continue + } + + // Line must be of the form + // func Open(path string, mode int, perm int) (fd int, errno error) + // Split into name, in params, out params. + f := regexp.MustCompile(`^\/\/sys(nb)? (\w+)\(([^()]*)\)\s*(?:\(([^()]+)\))?\s*(?:=\s*((?i)SYS_[A-Z0-9_]+))?$`).FindStringSubmatch(t) + if f == nil { + fmt.Fprintf(os.Stderr, "%s:%s\nmalformed //sys declaration\n", path, t) + os.Exit(1) + } + funct, inps, outps, sysname := f[2], f[3], f[4], f[5] + + // Split argument lists on comma. + in := parseParamList(inps) + out := parseParamList(outps) + + // Try in vain to keep people from editing this file. + // The theory is that they jump into the middle of the file + // without reading the header. + text += "// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT\n\n" + + // Go function header. + outDecl := "" + if len(out) > 0 { + outDecl = fmt.Sprintf(" (%s)", strings.Join(out, ", ")) + } + text += fmt.Sprintf("func %s(%s)%s {\n", funct, strings.Join(in, ", "), outDecl) + + // Check if err return available + errvar := "" + for _, param := range out { + p := parseParam(param) + if p.Type == "error" { + errvar = p.Name + break + } + } + + // Prepare arguments to Syscall. + var args []string + n := 0 + for _, param := range in { + p := parseParam(param) + if regexp.MustCompile(`^\*`).FindStringSubmatch(p.Type) != nil { + args = append(args, "uintptr(unsafe.Pointer("+p.Name+"))") + } else if p.Type == "string" && errvar != "" { + text += fmt.Sprintf("\tvar _p%d *byte\n", n) + text += fmt.Sprintf("\t_p%d, %s = BytePtrFromString(%s)\n", n, errvar, p.Name) + text += fmt.Sprintf("\tif %s != nil {\n\t\treturn\n\t}\n", errvar) + args = append(args, fmt.Sprintf("uintptr(unsafe.Pointer(_p%d))", n)) + n++ + } else if p.Type == "string" { + fmt.Fprintf(os.Stderr, path+":"+funct+" uses string arguments, but has no error return\n") + text += fmt.Sprintf("\tvar _p%d *byte\n", n) + text += fmt.Sprintf("\t_p%d, _ = BytePtrFromString(%s)\n", n, p.Name) + args = append(args, fmt.Sprintf("uintptr(unsafe.Pointer(_p%d))", n)) + n++ + } else if regexp.MustCompile(`^\[\](.*)`).FindStringSubmatch(p.Type) != nil { + // Convert slice into pointer, length. + // Have to be careful not to take address of &a[0] if len == 0: + // pass dummy pointer in that case. + // Used to pass nil, but some OSes or simulators reject write(fd, nil, 0). + text += fmt.Sprintf("\tvar _p%d unsafe.Pointer\n", n) + text += fmt.Sprintf("\tif len(%s) > 0 {\n\t\t_p%d = unsafe.Pointer(&%s[0])\n\t}", p.Name, n, p.Name) + text += fmt.Sprintf(" else {\n\t\t_p%d = unsafe.Pointer(&_zero)\n\t}\n", n) + args = append(args, fmt.Sprintf("uintptr(_p%d)", n), fmt.Sprintf("uintptr(len(%s))", p.Name)) + n++ + } else if p.Type == "int64" && (*openbsd || *netbsd) { + args = append(args, "0") + if endianness == "big-endian" { + args = append(args, fmt.Sprintf("uintptr(%s>>32)", p.Name), fmt.Sprintf("uintptr(%s)", p.Name)) + } else if endianness == "little-endian" { + args = append(args, fmt.Sprintf("uintptr(%s)", p.Name), fmt.Sprintf("uintptr(%s>>32)", p.Name)) + } else { + args = append(args, fmt.Sprintf("uintptr(%s)", p.Name)) + } + } else if p.Type == "int64" && *dragonfly { + if regexp.MustCompile(`^(?i)extp(read|write)`).FindStringSubmatch(funct) == nil { + args = append(args, "0") + } + if endianness == "big-endian" { + args = append(args, fmt.Sprintf("uintptr(%s>>32)", p.Name), fmt.Sprintf("uintptr(%s)", p.Name)) + } else if endianness == "little-endian" { + args = append(args, fmt.Sprintf("uintptr(%s)", p.Name), fmt.Sprintf("uintptr(%s>>32)", p.Name)) + } else { + args = append(args, fmt.Sprintf("uintptr(%s)", p.Name)) + } + } else if p.Type == "int64" && endianness != "" { + if len(args)%2 == 1 && *arm { + // arm abi specifies 64-bit argument uses + // (even, odd) pair + args = append(args, "0") + } + if endianness == "big-endian" { + args = append(args, fmt.Sprintf("uintptr(%s>>32)", p.Name), fmt.Sprintf("uintptr(%s)", p.Name)) + } else { + args = append(args, fmt.Sprintf("uintptr(%s)", p.Name), fmt.Sprintf("uintptr(%s>>32)", p.Name)) + } + } else { + args = append(args, fmt.Sprintf("uintptr(%s)", p.Name)) + } + } + + // Determine which form to use; pad args with zeros. + asm := "Syscall" + if nonblock != nil { + if errvar == "" && goos == "linux" { + asm = "RawSyscallNoError" + } else { + asm = "RawSyscall" + } + } else { + if errvar == "" && goos == "linux" { + asm = "SyscallNoError" + } + } + if len(args) <= 3 { + for len(args) < 3 { + args = append(args, "0") + } + } else if len(args) <= 6 { + asm += "6" + for len(args) < 6 { + args = append(args, "0") + } + } else if len(args) <= 9 { + asm += "9" + for len(args) < 9 { + args = append(args, "0") + } + } else { + fmt.Fprintf(os.Stderr, "%s:%s too many arguments to system call\n", path, funct) + } + + // System call number. + if sysname == "" { + sysname = "SYS_" + funct + sysname = regexp.MustCompile(`([a-z])([A-Z])`).ReplaceAllString(sysname, `${1}_$2`) + sysname = strings.ToUpper(sysname) + } + + // Actual call. + arglist := strings.Join(args, ", ") + call := fmt.Sprintf("%s(%s, %s)", asm, sysname, arglist) + + // Assign return values. + body := "" + ret := []string{"_", "_", "_"} + doErrno := false + for i := 0; i < len(out); i++ { + p := parseParam(out[i]) + reg := "" + if p.Name == "err" && !*plan9 { + reg = "e1" + ret[2] = reg + doErrno = true + } else if p.Name == "err" && *plan9 { + ret[0] = "r0" + ret[2] = "e1" + break + } else { + reg = fmt.Sprintf("r%d", i) + ret[i] = reg + } + if p.Type == "bool" { + reg = fmt.Sprintf("%s != 0", reg) + } + if p.Type == "int64" && endianness != "" { + // 64-bit number in r1:r0 or r0:r1. + if i+2 > len(out) { + fmt.Fprintf(os.Stderr, "%s:%s not enough registers for int64 return\n", path, funct) + } + if endianness == "big-endian" { + reg = fmt.Sprintf("int64(r%d)<<32 | int64(r%d)", i, i+1) + } else { + reg = fmt.Sprintf("int64(r%d)<<32 | int64(r%d)", i+1, i) + } + ret[i] = fmt.Sprintf("r%d", i) + ret[i+1] = fmt.Sprintf("r%d", i+1) + } + if reg != "e1" || *plan9 { + body += fmt.Sprintf("\t%s = %s(%s)\n", p.Name, p.Type, reg) + } + } + if ret[0] == "_" && ret[1] == "_" && ret[2] == "_" { + text += fmt.Sprintf("\t%s\n", call) + } else { + if errvar == "" && goos == "linux" { + // raw syscall without error on Linux, see golang.org/issue/22924 + text += fmt.Sprintf("\t%s, %s := %s\n", ret[0], ret[1], call) + } else { + text += fmt.Sprintf("\t%s, %s, %s := %s\n", ret[0], ret[1], ret[2], call) + } + } + text += body + + if *plan9 && ret[2] == "e1" { + text += "\tif int32(r0) == -1 {\n" + text += "\t\terr = e1\n" + text += "\t}\n" + } else if doErrno { + text += "\tif e1 != 0 {\n" + text += "\t\terr = errnoErr(e1)\n" + text += "\t}\n" + } + text += "\treturn\n" + text += "}\n\n" + + } + if err := s.Err(); err != nil { + fmt.Fprintf(os.Stderr, err.Error()) + os.Exit(1) + } + file.Close() + } + fmt.Printf(srcTemplate, cmdLine(), buildTags(), text) +} + +const srcTemplate = `// %s +// Code generated by the command above; see README.md. DO NOT EDIT. + +// +build %s + +package unix + +import ( + "syscall" + "unsafe" +) + +var _ syscall.Errno + +%s +` diff --git a/vendor/golang.org/x/sys/unix/mksyscall.pl b/vendor/golang.org/x/sys/unix/mksyscall.pl deleted file mode 100755 index 1f6b926f8..000000000 --- a/vendor/golang.org/x/sys/unix/mksyscall.pl +++ /dev/null @@ -1,341 +0,0 @@ -#!/usr/bin/env perl -# 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. - -# This program reads a file containing function prototypes -# (like syscall_darwin.go) and generates system call bodies. -# The prototypes are marked by lines beginning with "//sys" -# and read like func declarations if //sys is replaced by func, but: -# * The parameter lists must give a name for each argument. -# This includes return parameters. -# * The parameter lists must give a type for each argument: -# the (x, y, z int) shorthand is not allowed. -# * If the return parameter is an error number, it must be named errno. - -# A line beginning with //sysnb is like //sys, except that the -# goroutine will not be suspended during the execution of the system -# call. This must only be used for system calls which can never -# block, as otherwise the system call could cause all goroutines to -# hang. - -use strict; - -my $cmdline = "mksyscall.pl " . join(' ', @ARGV); -my $errors = 0; -my $_32bit = ""; -my $plan9 = 0; -my $openbsd = 0; -my $netbsd = 0; -my $dragonfly = 0; -my $arm = 0; # 64-bit value should use (even, odd)-pair -my $tags = ""; # build tags - -if($ARGV[0] eq "-b32") { - $_32bit = "big-endian"; - shift; -} elsif($ARGV[0] eq "-l32") { - $_32bit = "little-endian"; - shift; -} -if($ARGV[0] eq "-plan9") { - $plan9 = 1; - shift; -} -if($ARGV[0] eq "-openbsd") { - $openbsd = 1; - shift; -} -if($ARGV[0] eq "-netbsd") { - $netbsd = 1; - shift; -} -if($ARGV[0] eq "-dragonfly") { - $dragonfly = 1; - shift; -} -if($ARGV[0] eq "-arm") { - $arm = 1; - shift; -} -if($ARGV[0] eq "-tags") { - shift; - $tags = $ARGV[0]; - shift; -} - -if($ARGV[0] =~ /^-/) { - print STDERR "usage: mksyscall.pl [-b32 | -l32] [-tags x,y] [file ...]\n"; - exit 1; -} - -# Check that we are using the new build system if we should -if($ENV{'GOOS'} eq "linux" && $ENV{'GOARCH'} ne "sparc64") { - if($ENV{'GOLANG_SYS_BUILD'} ne "docker") { - print STDERR "In the new build system, mksyscall should not be called directly.\n"; - print STDERR "See README.md\n"; - exit 1; - } -} - - -sub parseparamlist($) { - my ($list) = @_; - $list =~ s/^\s*//; - $list =~ s/\s*$//; - if($list eq "") { - return (); - } - return split(/\s*,\s*/, $list); -} - -sub parseparam($) { - my ($p) = @_; - if($p !~ /^(\S*) (\S*)$/) { - print STDERR "$ARGV:$.: malformed parameter: $p\n"; - $errors = 1; - return ("xx", "int"); - } - return ($1, $2); -} - -my $text = ""; -while(<>) { - chomp; - s/\s+/ /g; - s/^\s+//; - s/\s+$//; - my $nonblock = /^\/\/sysnb /; - next if !/^\/\/sys / && !$nonblock; - - # Line must be of the form - # func Open(path string, mode int, perm int) (fd int, errno error) - # Split into name, in params, out params. - if(!/^\/\/sys(nb)? (\w+)\(([^()]*)\)\s*(?:\(([^()]+)\))?\s*(?:=\s*((?i)SYS_[A-Z0-9_]+))?$/) { - print STDERR "$ARGV:$.: malformed //sys declaration\n"; - $errors = 1; - next; - } - my ($func, $in, $out, $sysname) = ($2, $3, $4, $5); - - # Split argument lists on comma. - my @in = parseparamlist($in); - my @out = parseparamlist($out); - - # Try in vain to keep people from editing this file. - # The theory is that they jump into the middle of the file - # without reading the header. - $text .= "// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT\n\n"; - - # Go function header. - my $out_decl = @out ? sprintf(" (%s)", join(', ', @out)) : ""; - $text .= sprintf "func %s(%s)%s {\n", $func, join(', ', @in), $out_decl; - - # Check if err return available - my $errvar = ""; - foreach my $p (@out) { - my ($name, $type) = parseparam($p); - if($type eq "error") { - $errvar = $name; - last; - } - } - - # Prepare arguments to Syscall. - my @args = (); - my $n = 0; - foreach my $p (@in) { - my ($name, $type) = parseparam($p); - if($type =~ /^\*/) { - push @args, "uintptr(unsafe.Pointer($name))"; - } elsif($type eq "string" && $errvar ne "") { - $text .= "\tvar _p$n *byte\n"; - $text .= "\t_p$n, $errvar = BytePtrFromString($name)\n"; - $text .= "\tif $errvar != nil {\n\t\treturn\n\t}\n"; - push @args, "uintptr(unsafe.Pointer(_p$n))"; - $n++; - } elsif($type eq "string") { - print STDERR "$ARGV:$.: $func uses string arguments, but has no error return\n"; - $text .= "\tvar _p$n *byte\n"; - $text .= "\t_p$n, _ = BytePtrFromString($name)\n"; - push @args, "uintptr(unsafe.Pointer(_p$n))"; - $n++; - } elsif($type =~ /^\[\](.*)/) { - # Convert slice into pointer, length. - # Have to be careful not to take address of &a[0] if len == 0: - # pass dummy pointer in that case. - # Used to pass nil, but some OSes or simulators reject write(fd, nil, 0). - $text .= "\tvar _p$n unsafe.Pointer\n"; - $text .= "\tif len($name) > 0 {\n\t\t_p$n = unsafe.Pointer(\&${name}[0])\n\t}"; - $text .= " else {\n\t\t_p$n = unsafe.Pointer(&_zero)\n\t}"; - $text .= "\n"; - push @args, "uintptr(_p$n)", "uintptr(len($name))"; - $n++; - } elsif($type eq "int64" && ($openbsd || $netbsd)) { - push @args, "0"; - if($_32bit eq "big-endian") { - push @args, "uintptr($name>>32)", "uintptr($name)"; - } elsif($_32bit eq "little-endian") { - push @args, "uintptr($name)", "uintptr($name>>32)"; - } else { - push @args, "uintptr($name)"; - } - } elsif($type eq "int64" && $dragonfly) { - if ($func !~ /^extp(read|write)/i) { - push @args, "0"; - } - if($_32bit eq "big-endian") { - push @args, "uintptr($name>>32)", "uintptr($name)"; - } elsif($_32bit eq "little-endian") { - push @args, "uintptr($name)", "uintptr($name>>32)"; - } else { - push @args, "uintptr($name)"; - } - } elsif($type eq "int64" && $_32bit ne "") { - if(@args % 2 && $arm) { - # arm abi specifies 64-bit argument uses - # (even, odd) pair - push @args, "0" - } - if($_32bit eq "big-endian") { - push @args, "uintptr($name>>32)", "uintptr($name)"; - } else { - push @args, "uintptr($name)", "uintptr($name>>32)"; - } - } else { - push @args, "uintptr($name)"; - } - } - - # Determine which form to use; pad args with zeros. - my $asm = "Syscall"; - if ($nonblock) { - if ($errvar eq "" && $ENV{'GOOS'} eq "linux") { - $asm = "RawSyscallNoError"; - } else { - $asm = "RawSyscall"; - } - } else { - if ($errvar eq "" && $ENV{'GOOS'} eq "linux") { - $asm = "SyscallNoError"; - } - } - if(@args <= 3) { - while(@args < 3) { - push @args, "0"; - } - } elsif(@args <= 6) { - $asm .= "6"; - while(@args < 6) { - push @args, "0"; - } - } elsif(@args <= 9) { - $asm .= "9"; - while(@args < 9) { - push @args, "0"; - } - } else { - print STDERR "$ARGV:$.: too many arguments to system call\n"; - } - - # System call number. - if($sysname eq "") { - $sysname = "SYS_$func"; - $sysname =~ s/([a-z])([A-Z])/${1}_$2/g; # turn FooBar into Foo_Bar - $sysname =~ y/a-z/A-Z/; - } - - # Actual call. - my $args = join(', ', @args); - my $call = "$asm($sysname, $args)"; - - # Assign return values. - my $body = ""; - my @ret = ("_", "_", "_"); - my $do_errno = 0; - for(my $i=0; $i<@out; $i++) { - my $p = $out[$i]; - my ($name, $type) = parseparam($p); - my $reg = ""; - if($name eq "err" && !$plan9) { - $reg = "e1"; - $ret[2] = $reg; - $do_errno = 1; - } elsif($name eq "err" && $plan9) { - $ret[0] = "r0"; - $ret[2] = "e1"; - next; - } else { - $reg = sprintf("r%d", $i); - $ret[$i] = $reg; - } - if($type eq "bool") { - $reg = "$reg != 0"; - } - if($type eq "int64" && $_32bit ne "") { - # 64-bit number in r1:r0 or r0:r1. - if($i+2 > @out) { - print STDERR "$ARGV:$.: not enough registers for int64 return\n"; - } - if($_32bit eq "big-endian") { - $reg = sprintf("int64(r%d)<<32 | int64(r%d)", $i, $i+1); - } else { - $reg = sprintf("int64(r%d)<<32 | int64(r%d)", $i+1, $i); - } - $ret[$i] = sprintf("r%d", $i); - $ret[$i+1] = sprintf("r%d", $i+1); - } - if($reg ne "e1" || $plan9) { - $body .= "\t$name = $type($reg)\n"; - } - } - if ($ret[0] eq "_" && $ret[1] eq "_" && $ret[2] eq "_") { - $text .= "\t$call\n"; - } else { - if ($errvar eq "" && $ENV{'GOOS'} eq "linux") { - # raw syscall without error on Linux, see golang.org/issue/22924 - $text .= "\t$ret[0], $ret[1] := $call\n"; - } else { - $text .= "\t$ret[0], $ret[1], $ret[2] := $call\n"; - } - } - $text .= $body; - - if ($plan9 && $ret[2] eq "e1") { - $text .= "\tif int32(r0) == -1 {\n"; - $text .= "\t\terr = e1\n"; - $text .= "\t}\n"; - } elsif ($do_errno) { - $text .= "\tif e1 != 0 {\n"; - $text .= "\t\terr = errnoErr(e1)\n"; - $text .= "\t}\n"; - } - $text .= "\treturn\n"; - $text .= "}\n\n"; -} - -chomp $text; -chomp $text; - -if($errors) { - exit 1; -} - -print < 0 { + _p1 = unsafe.Pointer(&dest[0]) + } else { + _p1 = unsafe.Pointer(&_zero) + } + r0, _, e1 := Syscall6(SYS_FGETXATTR, uintptr(fd), uintptr(unsafe.Pointer(_p0)), uintptr(_p1), uintptr(len(dest)), 0, 0) + sz = int(r0) + if e1 != 0 { + err = errnoErr(e1) + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func FinitModule(fd int, params string, flags int) (err error) { + var _p0 *byte + _p0, err = BytePtrFromString(params) + if err != nil { + return + } + _, _, e1 := Syscall(SYS_FINIT_MODULE, uintptr(fd), uintptr(unsafe.Pointer(_p0)), uintptr(flags)) + if e1 != 0 { + err = errnoErr(e1) + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func Flistxattr(fd int, dest []byte) (sz int, err error) { + var _p0 unsafe.Pointer + if len(dest) > 0 { + _p0 = unsafe.Pointer(&dest[0]) + } else { + _p0 = unsafe.Pointer(&_zero) + } + r0, _, e1 := Syscall(SYS_FLISTXATTR, uintptr(fd), uintptr(_p0), uintptr(len(dest))) + sz = int(r0) + if e1 != 0 { + err = errnoErr(e1) + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + func Flock(fd int, how int) (err error) { _, _, e1 := Syscall(SYS_FLOCK, uintptr(fd), uintptr(how), 0) if e1 != 0 { @@ -599,6 +663,42 @@ func Flock(fd int, how int) (err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT +func Fremovexattr(fd int, attr string) (err error) { + var _p0 *byte + _p0, err = BytePtrFromString(attr) + if err != nil { + return + } + _, _, e1 := Syscall(SYS_FREMOVEXATTR, uintptr(fd), uintptr(unsafe.Pointer(_p0)), 0) + if e1 != 0 { + err = errnoErr(e1) + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func Fsetxattr(fd int, attr string, dest []byte, flags int) (err error) { + var _p0 *byte + _p0, err = BytePtrFromString(attr) + if err != nil { + return + } + var _p1 unsafe.Pointer + if len(dest) > 0 { + _p1 = unsafe.Pointer(&dest[0]) + } else { + _p1 = unsafe.Pointer(&_zero) + } + _, _, e1 := Syscall6(SYS_FSETXATTR, uintptr(fd), uintptr(unsafe.Pointer(_p0)), uintptr(_p1), uintptr(len(dest)), uintptr(flags), 0) + if e1 != 0 { + err = errnoErr(e1) + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + func Fsync(fd int) (err error) { _, _, e1 := Syscall(SYS_FSYNC, uintptr(fd), 0, 0) if e1 != 0 { @@ -737,6 +837,27 @@ func Getxattr(path string, attr string, dest []byte) (sz int, err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT +func InitModule(moduleImage []byte, params string) (err error) { + var _p0 unsafe.Pointer + if len(moduleImage) > 0 { + _p0 = unsafe.Pointer(&moduleImage[0]) + } else { + _p0 = unsafe.Pointer(&_zero) + } + var _p1 *byte + _p1, err = BytePtrFromString(params) + if err != nil { + return + } + _, _, e1 := Syscall(SYS_INIT_MODULE, uintptr(_p0), uintptr(len(moduleImage)), uintptr(unsafe.Pointer(_p1))) + if e1 != 0 { + err = errnoErr(e1) + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + func InotifyAddWatch(fd int, pathname string, mask uint32) (watchdesc int, err error) { var _p0 *byte _p0, err = BytePtrFromString(pathname) @@ -919,6 +1040,22 @@ func Lsetxattr(path string, attr string, data []byte, flags int) (err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT +func MemfdCreate(name string, flags int) (fd int, err error) { + var _p0 *byte + _p0, err = BytePtrFromString(name) + if err != nil { + return + } + r0, _, e1 := Syscall(SYS_MEMFD_CREATE, uintptr(unsafe.Pointer(_p0)), uintptr(flags), 0) + fd = int(r0) + if e1 != 0 { + err = errnoErr(e1) + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + func Mkdirat(dirfd int, path string, mode uint32) (err error) { var _p0 *byte _p0, err = BytePtrFromString(path) @@ -1078,6 +1215,26 @@ func Renameat(olddirfd int, oldpath string, newdirfd int, newpath string) (err e // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT +func Renameat2(olddirfd int, oldpath string, newdirfd int, newpath string, flags uint) (err error) { + var _p0 *byte + _p0, err = BytePtrFromString(oldpath) + if err != nil { + return + } + var _p1 *byte + _p1, err = BytePtrFromString(newpath) + if err != nil { + return + } + _, _, e1 := Syscall6(SYS_RENAMEAT2, uintptr(olddirfd), uintptr(unsafe.Pointer(_p0)), uintptr(newdirfd), uintptr(unsafe.Pointer(_p1)), uintptr(flags), 0) + if e1 != 0 { + err = errnoErr(e1) + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + func RequestKey(keyType string, description string, callback string, destRingid int) (id int, err error) { var _p0 *byte _p0, err = BytePtrFromString(keyType) @@ -1489,6 +1646,21 @@ func Munlockall() (err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT +func faccessat(dirfd int, path string, mode uint32) (err error) { + var _p0 *byte + _p0, err = BytePtrFromString(path) + if err != nil { + return + } + _, _, e1 := Syscall(SYS_FACCESSAT, uintptr(dirfd), uintptr(unsafe.Pointer(_p0)), uintptr(mode)) + if e1 != 0 { + err = errnoErr(e1) + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + func EpollWait(epfd int, events []EpollEvent, msec int) (n int, err error) { var _p0 unsafe.Pointer if len(events) > 0 { diff --git a/vendor/golang.org/x/sys/unix/zsyscall_netbsd_386.go b/vendor/golang.org/x/sys/unix/zsyscall_netbsd_386.go index 9ed7c71fb..642db7670 100644 --- a/vendor/golang.org/x/sys/unix/zsyscall_netbsd_386.go +++ b/vendor/golang.org/x/sys/unix/zsyscall_netbsd_386.go @@ -1,4 +1,4 @@ -// mksyscall.pl -l32 -netbsd -tags netbsd,386 syscall_bsd.go syscall_netbsd.go syscall_netbsd_386.go +// go run mksyscall.go -l32 -netbsd -tags netbsd,386 syscall_bsd.go syscall_netbsd.go syscall_netbsd_386.go // Code generated by the command above; see README.md. DO NOT EDIT. // +build netbsd,386 diff --git a/vendor/golang.org/x/sys/unix/zsyscall_netbsd_amd64.go b/vendor/golang.org/x/sys/unix/zsyscall_netbsd_amd64.go index 613b7fd99..59585fee3 100644 --- a/vendor/golang.org/x/sys/unix/zsyscall_netbsd_amd64.go +++ b/vendor/golang.org/x/sys/unix/zsyscall_netbsd_amd64.go @@ -1,4 +1,4 @@ -// mksyscall.pl -netbsd -tags netbsd,amd64 syscall_bsd.go syscall_netbsd.go syscall_netbsd_amd64.go +// go run mksyscall.go -netbsd -tags netbsd,amd64 syscall_bsd.go syscall_netbsd.go syscall_netbsd_amd64.go // Code generated by the command above; see README.md. DO NOT EDIT. // +build netbsd,amd64 diff --git a/vendor/golang.org/x/sys/unix/zsyscall_netbsd_arm.go b/vendor/golang.org/x/sys/unix/zsyscall_netbsd_arm.go index 084750878..6ec31434b 100644 --- a/vendor/golang.org/x/sys/unix/zsyscall_netbsd_arm.go +++ b/vendor/golang.org/x/sys/unix/zsyscall_netbsd_arm.go @@ -1,4 +1,4 @@ -// mksyscall.pl -l32 -netbsd -arm -tags netbsd,arm syscall_bsd.go syscall_netbsd.go syscall_netbsd_arm.go +// go run mksyscall.go -l32 -netbsd -arm -tags netbsd,arm syscall_bsd.go syscall_netbsd.go syscall_netbsd_arm.go // Code generated by the command above; see README.md. DO NOT EDIT. // +build netbsd,arm diff --git a/vendor/golang.org/x/sys/unix/zsyscall_openbsd_386.go b/vendor/golang.org/x/sys/unix/zsyscall_openbsd_386.go index 414cd13c8..6a489fac0 100644 --- a/vendor/golang.org/x/sys/unix/zsyscall_openbsd_386.go +++ b/vendor/golang.org/x/sys/unix/zsyscall_openbsd_386.go @@ -1,4 +1,4 @@ -// mksyscall.pl -l32 -openbsd -tags openbsd,386 syscall_bsd.go syscall_openbsd.go syscall_openbsd_386.go +// go run mksyscall.go -l32 -openbsd -tags openbsd,386 syscall_bsd.go syscall_openbsd.go syscall_openbsd_386.go // Code generated by the command above; see README.md. DO NOT EDIT. // +build openbsd,386 diff --git a/vendor/golang.org/x/sys/unix/zsyscall_openbsd_amd64.go b/vendor/golang.org/x/sys/unix/zsyscall_openbsd_amd64.go index 846f5fa64..30cba4347 100644 --- a/vendor/golang.org/x/sys/unix/zsyscall_openbsd_amd64.go +++ b/vendor/golang.org/x/sys/unix/zsyscall_openbsd_amd64.go @@ -1,4 +1,4 @@ -// mksyscall.pl -openbsd -tags openbsd,amd64 syscall_bsd.go syscall_openbsd.go syscall_openbsd_amd64.go +// go run mksyscall.go -openbsd -tags openbsd,amd64 syscall_bsd.go syscall_openbsd.go syscall_openbsd_amd64.go // Code generated by the command above; see README.md. DO NOT EDIT. // +build openbsd,amd64 diff --git a/vendor/golang.org/x/sys/unix/zsyscall_openbsd_arm.go b/vendor/golang.org/x/sys/unix/zsyscall_openbsd_arm.go index 59911659d..fa1beda33 100644 --- a/vendor/golang.org/x/sys/unix/zsyscall_openbsd_arm.go +++ b/vendor/golang.org/x/sys/unix/zsyscall_openbsd_arm.go @@ -1,4 +1,4 @@ -// mksyscall.pl -l32 -openbsd -arm -tags openbsd,arm syscall_bsd.go syscall_openbsd.go syscall_openbsd_arm.go +// go run mksyscall.go -l32 -openbsd -arm -tags openbsd,arm syscall_bsd.go syscall_openbsd.go syscall_openbsd_arm.go // Code generated by the command above; see README.md. DO NOT EDIT. // +build openbsd,arm diff --git a/vendor/gopkg.in/yaml.v2/encode.go b/vendor/gopkg.in/yaml.v2/encode.go index a14435e82..0ee738e11 100644 --- a/vendor/gopkg.in/yaml.v2/encode.go +++ b/vendor/gopkg.in/yaml.v2/encode.go @@ -13,6 +13,19 @@ import ( "unicode/utf8" ) +// jsonNumber is the interface of the encoding/json.Number datatype. +// Repeating the interface here avoids a dependency on encoding/json, and also +// supports other libraries like jsoniter, which use a similar datatype with +// the same interface. Detecting this interface is useful when dealing with +// structures containing json.Number, which is a string under the hood. The +// encoder should prefer the use of Int64(), Float64() and string(), in that +// order, when encoding this type. +type jsonNumber interface { + Float64() (float64, error) + Int64() (int64, error) + String() string +} + type encoder struct { emitter yaml_emitter_t event yaml_event_t @@ -89,6 +102,21 @@ func (e *encoder) marshal(tag string, in reflect.Value) { } iface := in.Interface() switch m := iface.(type) { + case jsonNumber: + integer, err := m.Int64() + if err == nil { + // In this case the json.Number is a valid int64 + in = reflect.ValueOf(integer) + break + } + float, err := m.Float64() + if err == nil { + // In this case the json.Number is a valid float64 + in = reflect.ValueOf(float) + break + } + // fallback case - no number could be obtained + in = reflect.ValueOf(m.String()) case time.Time, *time.Time: // Although time.Time implements TextMarshaler, // we don't want to treat it as a string for YAML From dbde7fdc19fb284e9e34535668d6d5460a5e6e62 Mon Sep 17 00:00:00 2001 From: Qi Xiao Date: Mon, 3 Dec 2018 12:00:17 +0800 Subject: [PATCH 18/24] Update xenomint query filter to use ParseMulti instead of custom parsing --- xenomint/state.go | 49 +++++++++++++++++++---------------------------- 1 file changed, 20 insertions(+), 29 deletions(-) diff --git a/xenomint/state.go b/xenomint/state.go index 8599cf012..cb4e0ee1f 100644 --- a/xenomint/state.go +++ b/xenomint/state.go @@ -18,7 +18,6 @@ package xenomint import ( "database/sql" - "io" "strings" "sync" "sync/atomic" @@ -126,42 +125,34 @@ func (s *State) Close(commit bool) (err error) { func convertQueryAndBuildArgs(pattern string, args []types.NamedArg) (containsDDL bool, p string, ifs []interface{}, err error) { var ( tokenizer = sqlparser.NewStringTokenizer(pattern) - stmt sqlparser.Statement - lastPos int - query string queryParts []string + statements []sqlparser.Statement + i int + origQuery string + query string ) - for { - stmt, err = sqlparser.ParseNext(tokenizer) - - if err != nil && err != io.EOF { - return - } - - if err == io.EOF { - err = nil - break - } - - query = pattern[lastPos : tokenizer.Position-1] - lastPos = tokenizer.Position + 1 + if queryParts, statements, err = sqlparser.ParseMultiple(tokenizer); err != nil { + err = errors.Wrap(err, "parse sql failed") + return + } - // translate show statement - if showStmt, ok := stmt.(*sqlparser.Show); ok { - origQuery := query + for i = range queryParts { + switch stmt := statements[i].(type) { + case *sqlparser.Show: + origQuery = queryParts[i] - switch showStmt.Type { + switch stmt.Type { case "table": - if showStmt.ShowCreate { + if stmt.ShowCreate { query = "SELECT sql FROM sqlite_master WHERE type = \"table\" AND tbl_name = \"" + - showStmt.OnTable.Name.String() + "\"" + stmt.OnTable.Name.String() + "\"" } else { - query = "PRAGMA table_info(" + showStmt.OnTable.Name.String() + ")" + query = "PRAGMA table_info(" + stmt.OnTable.Name.String() + ")" } case "index": query = "SELECT name FROM sqlite_master WHERE type = \"index\" AND tbl_name = \"" + - showStmt.OnTable.Name.String() + "\"" + stmt.OnTable.Name.String() + "\"" case "tables": query = "SELECT name FROM sqlite_master WHERE type = \"table\"" } @@ -170,11 +161,11 @@ func convertQueryAndBuildArgs(pattern string, args []types.NamedArg) (containsDD "from": origQuery, "to": query, }).Debug("query translated") - } else if _, ok := stmt.(*sqlparser.DDL); ok { + + queryParts[i] = query + case *sqlparser.DDL: containsDDL = true } - - queryParts = append(queryParts, query) } p = strings.Join(queryParts, "; ") From f47ed08ce896fb22671f8aa376ca960348bbd53a Mon Sep 17 00:00:00 2001 From: Qi Xiao Date: Mon, 3 Dec 2018 12:01:13 +0800 Subject: [PATCH 19/24] Fix bugs and complete secure gateway --- cmd/cql-mysql-adapter/cursor/cursor.go | 14 +- cmd/cql-mysql-adapter/cursor/handler.go | 15 +- cmd/cql-mysql-adapter/cursor/scanner.go | 3 +- cmd/cql-mysql-adapter/handler/handler.go | 15 +- cmd/cql-mysql-adapter/resolver/resolver.go | 8 +- cmd/cql-mysql-adapter/server.go | 6 +- cmd/cql-secure-gateway/casbin/adapter.go | 214 ++++-- cmd/cql-secure-gateway/casbin/adapter_test.go | 369 ++++------ cmd/cql-secure-gateway/casbin/casbin.go | 38 +- cmd/cql-secure-gateway/casbin/util_test.go | 55 ++ cmd/cql-secure-gateway/casbin/utils.go | 88 +++ cmd/cql-secure-gateway/config.go | 141 ++-- cmd/cql-secure-gateway/config_test.go | 56 +- cmd/cql-secure-gateway/errors.go | 10 + cmd/cql-secure-gateway/handler.go | 639 ++++++++++++++++++ cmd/cql-secure-gateway/resolver.go | 101 ++- cmd/cql-secure-gateway/server.go | 6 +- 17 files changed, 1305 insertions(+), 473 deletions(-) create mode 100644 cmd/cql-secure-gateway/casbin/util_test.go create mode 100644 cmd/cql-secure-gateway/casbin/utils.go create mode 100644 cmd/cql-secure-gateway/handler.go diff --git a/cmd/cql-mysql-adapter/cursor/cursor.go b/cmd/cql-mysql-adapter/cursor/cursor.go index 66deef8e4..2f68c071e 100644 --- a/cmd/cql-mysql-adapter/cursor/cursor.go +++ b/cmd/cql-mysql-adapter/cursor/cursor.go @@ -29,7 +29,7 @@ import ( ) var ( - dbIDRegex = regexp.MustCompile("^[a-zA-Z0-9_\\.]+$") + dbIDRegex = regexp.MustCompile("^[a-zA-Z0-9_]+$") specialSelectQuery = regexp.MustCompile("^(?i)SELECT\\s+(DATABASE|USER)\\(\\)\\s*;?\\s*$") emptyResultQuery = regexp.MustCompile("^(?i)\\s*(?:/\\*.*?\\*/)?\\s*(?:SET|ROLLBACK).*$") emptyResultWithResultSetQuery = regexp.MustCompile("^(?i)\\s*(?:/\\*.*?\\*/)?\\s*(?:(?:SELECT\\s+)?@@(?:\\w+\\.)?|SHOW\\s+WARNINGS).*$") @@ -62,7 +62,7 @@ func NewCursor(h Handler) (c *Cursor) { return &Cursor{h: h} } -func (c *Cursor) buildResultSet(rows *sql.Rows) (r *my.Result, err error) { +func (c *Cursor) buildResultSet(rows Rows) (r *my.Result, err error) { // get columns var columns []string if columns, err = rows.Columns(); err != nil { @@ -283,14 +283,14 @@ func (c *Cursor) HandleQuery(query string) (r *my.Result, err error) { var q Query - if q, err = c.h.Resolve(c.getCurDB(), query); err != nil { + if q, err = c.h.Resolve(c.curUser, c.getCurDB(), query); err != nil { err = my.NewError(my.ER_SYNTAX_ERROR, err.Error()) return } // normal query if q.IsRead() { - var rows *sql.Rows + var rows Rows if rows, err = c.h.Query(q); err != nil { err = my.NewError(my.ER_UNKNOWN_ERROR, err.Error()) return @@ -322,7 +322,7 @@ func (c *Cursor) HandleQuery(query string) (r *my.Result, err error) { // HandleFieldList handle COM_FILED_LIST command. func (c *Cursor) HandleFieldList(table string, fieldWildcard string) (fields []*my.Field, err error) { // send show tables command - var columns *sql.Rows + var columns Rows if columns, err = c.h.QueryString(c.getCurDB(), fmt.Sprintf("DESC `%s`", table)); err != nil { // wrap error err = my.NewError(my.ER_UNKNOWN_ERROR, err.Error()) @@ -400,7 +400,7 @@ func (c *Cursor) HandleStmtPrepare(query string) (params int, columns int, conte var q Query - if q, err = c.h.Resolve(c.getCurDB(), query); err != nil { + if q, err = c.h.Resolve(c.curUser, c.getCurDB(), query); err != nil { err = my.NewError(my.ER_SYNTAX_ERROR, err.Error()) return } @@ -435,7 +435,7 @@ func (c *Cursor) HandleStmtExecute(context interface{}, query string, args []int // normal query if q.IsRead() { - var rows *sql.Rows + var rows Rows if rows, err = c.h.Query(q, args...); err != nil { err = my.NewError(my.ER_UNKNOWN_ERROR, err.Error()) return diff --git a/cmd/cql-mysql-adapter/cursor/handler.go b/cmd/cql-mysql-adapter/cursor/handler.go index 025de4da4..57e68ee7a 100644 --- a/cmd/cql-mysql-adapter/cursor/handler.go +++ b/cmd/cql-mysql-adapter/cursor/handler.go @@ -35,12 +35,21 @@ type Query interface { GetResultColumnCount() int } +// Rows define mocked rows for handler to process. +type Rows interface { + Close() error + Columns() ([]string, error) + Err() error + Next() bool + Scan(...interface{}) error +} + // Handler defines callback function utilized by cursor object. type Handler interface { EnsureDatabase(dbID string) error - Resolve(dbID string, query string) (Query, error) - Query(q Query, args ...interface{}) (*sql.Rows, error) + Resolve(user string, dbID string, query string) (Query, error) + Query(q Query, args ...interface{}) (Rows, error) Exec(q Query, args ...interface{}) (sql.Result, error) - QueryString(dbID string, query string, args ...interface{}) (*sql.Rows, error) + QueryString(dbID string, query string, args ...interface{}) (Rows, error) ExecString(dbID string, query string, args ...interface{}) (sql.Result, error) } diff --git a/cmd/cql-mysql-adapter/cursor/scanner.go b/cmd/cql-mysql-adapter/cursor/scanner.go index 5fd81dbd3..9ce786a85 100644 --- a/cmd/cql-mysql-adapter/cursor/scanner.go +++ b/cmd/cql-mysql-adapter/cursor/scanner.go @@ -17,7 +17,6 @@ package cursor import ( - "database/sql" "io" "time" ) @@ -82,7 +81,7 @@ func (s *rowScanner) ScanArgs() []interface{} { return s.scanArgs } -func readAllRows(rows *sql.Rows) (result [][]interface{}, err error) { +func readAllRows(rows Rows) (result [][]interface{}, err error) { var columns []string if columns, err = rows.Columns(); err != nil { return diff --git a/cmd/cql-mysql-adapter/handler/handler.go b/cmd/cql-mysql-adapter/handler/handler.go index bb0bf3a58..e1856e792 100644 --- a/cmd/cql-mysql-adapter/handler/handler.go +++ b/cmd/cql-mysql-adapter/handler/handler.go @@ -14,7 +14,7 @@ * limitations under the License. */ -package main +package handler import ( "database/sql" @@ -39,6 +39,13 @@ func NewHandler() *Handler { } } +// NewHandlerWithResolver returns the new mysql handler with resolver. +func NewHandlerWithResolver(r *resolver.Resolver) *Handler { + return &Handler{ + r: r, + } +} + // EnsureDatabase returns whether a database is valid or not. func (h *Handler) EnsureDatabase(dbID string) (err error) { _, err = h.ensureDatabase(dbID) @@ -46,7 +53,7 @@ func (h *Handler) EnsureDatabase(dbID string) (err error) { } // Resolve resolves query to ast and various parse results. -func (h *Handler) Resolve(dbID string, query string) (q cursor.Query, err error) { +func (h *Handler) Resolve(user string, dbID string, query string) (q cursor.Query, err error) { if _, err = h.ensureDatabase(dbID); err != nil { return } @@ -57,7 +64,7 @@ func (h *Handler) Resolve(dbID string, query string) (q cursor.Query, err error) } // Query executes a resolved read query. -func (h *Handler) Query(q cursor.Query, args ...interface{}) (rows *sql.Rows, err error) { +func (h *Handler) Query(q cursor.Query, args ...interface{}) (rows cursor.Rows, err error) { if !q.IsRead() { err = errors.Wrapf(resolver.ErrQueryLogicError, "not a read query") return @@ -78,7 +85,7 @@ func (h *Handler) Exec(q cursor.Query, args ...interface{}) (res sql.Result, err } // QueryString executes a string query without resolving. -func (h *Handler) QueryString(dbID string, query string, args ...interface{}) (rows *sql.Rows, err error) { +func (h *Handler) QueryString(dbID string, query string, args ...interface{}) (rows cursor.Rows, err error) { var db resolver.DBHandler if db, err = h.ensureDatabase(dbID); err != nil { return diff --git a/cmd/cql-mysql-adapter/resolver/resolver.go b/cmd/cql-mysql-adapter/resolver/resolver.go index 4dbc5483e..362864cf6 100644 --- a/cmd/cql-mysql-adapter/resolver/resolver.go +++ b/cmd/cql-mysql-adapter/resolver/resolver.go @@ -19,6 +19,7 @@ package resolver import ( "fmt" "io" + "strings" "sync/atomic" "github.com/CovenantSQL/sqlparser" @@ -251,6 +252,9 @@ func (r *Resolver) resolveQuery(dbID string, query string, maxQueries int) (quer queryCount int ) + // resolve :name to ValArg, ? to PosArg type + tokenizer.SeparatePositionalArgs = true + for { stmt, err = sqlparser.ParseNext(tokenizer) @@ -274,8 +278,8 @@ func (r *Resolver) resolveQuery(dbID string, query string, maxQueries int) (quer return } - q.Query = query[lastPos : tokenizer.Position-1] - lastPos = tokenizer.Position + 1 + q.Query = strings.TrimSpace(query[lastPos : tokenizer.Position-1]) + lastPos = tokenizer.Position queries = append(queries, q) } diff --git a/cmd/cql-mysql-adapter/server.go b/cmd/cql-mysql-adapter/server.go index 6e3142529..756e99e9f 100644 --- a/cmd/cql-mysql-adapter/server.go +++ b/cmd/cql-mysql-adapter/server.go @@ -20,6 +20,7 @@ import ( "net" "github.com/CovenantSQL/CovenantSQL/cmd/cql-mysql-adapter/cursor" + "github.com/CovenantSQL/CovenantSQL/cmd/cql-mysql-adapter/handler" "github.com/CovenantSQL/CovenantSQL/utils/log" mys "github.com/siddontang/go-mysql/server" ) @@ -28,7 +29,7 @@ import ( type Server struct { listenAddr string listener net.Listener - h *Handler + h *handler.Handler mysqlUser string mysqlPassword string } @@ -39,7 +40,7 @@ func NewServer(listenAddr string, user string, password string) (s *Server, err listenAddr: listenAddr, mysqlUser: user, mysqlPassword: password, - h: NewHandler(), + h: handler.NewHandler(), } if s.listener, err = net.Listen("tcp", listenAddr); err != nil { @@ -84,4 +85,5 @@ func (s *Server) handleConn(conn net.Conn) { // Shutdown ends the server. func (s *Server) Shutdown() { s.listener.Close() + s.h.Close() } diff --git a/cmd/cql-secure-gateway/casbin/adapter.go b/cmd/cql-secure-gateway/casbin/adapter.go index 009160bd1..d841a85ff 100644 --- a/cmd/cql-secure-gateway/casbin/adapter.go +++ b/cmd/cql-secure-gateway/casbin/adapter.go @@ -34,24 +34,163 @@ var ( ErrEmptyConfigItem = errors.New("empty config item") ) +// Field defines a single database column. +type Field struct { + Database string + Table string + Column string +} + +// NewField creates new field object instance. +func NewField(db, table, col string) (f *Field, err error) { + db = strings.TrimSpace(db) + table = strings.TrimSpace(table) + col = strings.TrimSpace(col) + + for _, s := range []string{db, table, col} { + if s == "" { + err = errors.Wrapf(ErrInvalidField, + "invalid field: %s, /
/ parts should not be blanked", f) + return + } + } + + f = &Field{ + Database: db, + Table: table, + Column: col, + } + + return +} + +// NewFieldFromString creates new field object instance from string field representation. +func NewFieldFromString(fieldStr string) (f *Field, err error) { + f = &Field{} + err = f.loadFromStr(fieldStr) + return +} + +// MatchesString test if string field matches. +func (f *Field) MatchesString(fieldStr string) bool { + var ( + field *Field + err error + ) + if field, err = NewFieldFromString(fieldStr); err != nil { + // ignore error + return false + } + return f.MatchesField(field) +} + +// MatchesField test if field matches. +func (f *Field) MatchesField(field *Field) bool { + if f == nil || field == nil { + return false + } + + var s1, s2 string + for i := 0; i != 3; i++ { + switch i { + case 0: + s1 = f.Database + s2 = field.Database + case 1: + s1 = f.Table + s2 = field.Table + case 2: + s1 = f.Column + s2 = field.Column + } + + if !tryGlobMatch(s1, s2) { + return false + } + } + + return true +} + +// IsWildcard returns whether the field is a wildcard or plain. +func (f *Field) IsWildcard() bool { + if f == nil { + return false + } + + return strings.ContainsRune(f.Database, '*') || + strings.ContainsRune(f.Table, '*') || + strings.ContainsRune(f.Column, '*') +} + +// UnmarshalYAML implements yaml.Unmarshaler interface. +func (f *Field) UnmarshalYAML(unmarshal func(interface{}) error) (err error) { + var fieldStr string + + if err = unmarshal(&fieldStr); err != nil { + return + } + + return f.loadFromStr(fieldStr) +} + +func (f *Field) loadFromStr(fieldStr string) (err error) { + var parts []string + + // split field into parts + parts = strings.Split(fieldStr, ".") + if len(parts) != 3 { + err = errors.Wrapf(ErrInvalidField, + "invalid field: %s, should be in form \".
.\", wildcard is supported", f) + return + } + + for i, p := range parts { + p = strings.TrimSpace(p) + + if p == "" { + err = errors.Wrapf(ErrInvalidField, + "invalid field: %s, /
/ parts should not be blanked", f) + return + } + + switch i { + case 0: + f.Database = p + case 1: + f.Table = p + case 2: + f.Column = p + } + } + + return +} + +// MarshalYAML implements yaml.Marshaler interface. +func (f Field) MarshalYAML() (interface{}, error) { + return f.String(), nil +} + +// String returns the string representation of field. +func (f *Field) String() string { + return strings.ToLower(fmt.Sprintf("%s.%s.%s", f.Database, f.Table, f.Column)) +} + // Policy defines single resource policy. type Policy struct { User string `yaml:"User"` - Field string `yaml:"Field"` + Field Field `yaml:"Field"` Action string `yaml:"Action"` } // UserGroup defines single user group relation. type UserGroup map[string][]string -// FieldGroup defines single field group relation. -type FieldGroup map[string][]string - // Config defines the rules config for casbin adapter. type Config struct { - Policies []Policy `yaml:"Policies"` - UserGroup UserGroup `yaml:"UserGroups"` - FieldGroup FieldGroup `yaml:"FieldGroups"` + Policies []Policy `yaml:"Policies"` + UserGroup UserGroup `yaml:"UserGroups"` } // Adapter defines a adapter for casbin to access rules. @@ -81,17 +220,10 @@ func NewAdapter(cfg *Config) (a *Adapter, err error) { func (a *Adapter) validate() (err error) { // validate non-group fields for _, p := range a.cfg.Policies { - if p.User == "" || p.Field == "" || p.Action == "" { + if p.User == "" || p.Action == "" { err = errors.Wrapf(ErrEmptyConfigItem, "%#v contains empty field", p) return } - - if _, exists := a.cfg.FieldGroup[p.Field]; !exists { - // must be a valid field - if err = a.validateField(p.Field); err != nil { - return - } - } } for userGroup, users := range a.cfg.UserGroup { @@ -108,48 +240,6 @@ func (a *Adapter) validate() (err error) { } } - // validate fields - for fieldGroup, fields := range a.cfg.FieldGroup { - if fieldGroup == "" { - err = errors.Wrapf(ErrEmptyConfigItem, "field group name should not be empty") - return - } - - // should all be valid field - for _, f := range fields { - if f == "" { - err = errors.Wrapf(ErrEmptyConfigItem, "field name should not be empty") - return - } - - if err = a.validateField(f); err != nil { - return - } - } - } - - return -} - -func (a *Adapter) validateField(f string) (err error) { - parts := strings.Split(f, ".") - - if len(parts) != 3 { - err = errors.Wrapf(ErrInvalidField, - "invalid field: %s, should be in form \".
.\", wildcard is supported", f) - return - } - - for _, p := range parts { - p = strings.TrimSpace(p) - - if p == "" { - err = errors.Wrapf(ErrInvalidField, - "invalid field: %s, /
/ parts should not be blanked", f) - return - } - } - return } @@ -159,7 +249,7 @@ func (a *Adapter) LoadPolicy(model model.Model) (err error) { // load policy for _, p := range a.cfg.Policies { - line := fmt.Sprintf("p, %s, %s, %s", p.User, p.Field, p.Action) + line := fmt.Sprintf("p, %s, %s, %s", p.User, p.Field.String(), p.Action) persist.LoadPolicyLine(line, model) } @@ -171,14 +261,6 @@ func (a *Adapter) LoadPolicy(model model.Model) (err error) { } } - // load field groups - for fieldGroup, fields := range a.cfg.FieldGroup { - for _, field := range fields { - line := fmt.Sprintf("g2, %s, %s", field, fieldGroup) - persist.LoadPolicyLine(line, model) - } - } - return } diff --git a/cmd/cql-secure-gateway/casbin/adapter_test.go b/cmd/cql-secure-gateway/casbin/adapter_test.go index 8f313e538..f42ab7436 100644 --- a/cmd/cql-secure-gateway/casbin/adapter_test.go +++ b/cmd/cql-secure-gateway/casbin/adapter_test.go @@ -22,30 +22,111 @@ import ( "github.com/casbin/casbin" "github.com/pkg/errors" . "github.com/smartystreets/goconvey/convey" + "gopkg.in/yaml.v2" ) +func mustNewField(c C, fieldStr string) (f Field) { + var ( + err error + pf *Field + ) + pf, err = NewFieldFromString(fieldStr) + f = *pf + c.So(err, ShouldBeNil) + return +} + +func TestNewField(t *testing.T) { + Convey("test new field", t, func() { + var ( + f *Field + err error + ) + f, err = NewFieldFromString("db1.tbl1.col1") + So(f, ShouldNotBeNil) + So(err, ShouldBeNil) + So(f.Database, ShouldEqual, "db1") + So(f.Table, ShouldEqual, "tbl1") + So(f.Column, ShouldEqual, "col1") + So(f.String(), ShouldEqual, "db1.tbl1.col1") + + // test marshal unmarshal + var yamlBytes []byte + yamlBytes, err = yaml.Marshal(f) + So(err, ShouldBeNil) + So(yamlBytes, ShouldNotBeEmpty) + + var uField *Field + err = yaml.Unmarshal(yamlBytes, &uField) + So(err, ShouldBeNil) + So(uField, ShouldResemble, f) + + // test load invalid field + _, err = NewFieldFromString("") + So(err, ShouldNotBeNil) + + _, err = NewFieldFromString(" ") + So(err, ShouldNotBeNil) + + _, err = NewFieldFromString("db1") + So(err, ShouldNotBeNil) + + _, err = NewFieldFromString("db1.tbl1") + So(err, ShouldNotBeNil) + + _, err = NewFieldFromString("db1..col1") + So(err, ShouldNotBeNil) + + _, err = NewFieldFromString("db1.col1.") + So(err, ShouldNotBeNil) + + _, err = NewFieldFromString(".tbl1.col1") + So(err, ShouldNotBeNil) + + // trivial cases + f, err = NewFieldFromString("a . b . c") + So(err, ShouldBeNil) + So(f.Database, ShouldEqual, "a") + So(f.Table, ShouldEqual, "b") + So(f.Column, ShouldEqual, "c") + + // invalid marshal + yamlBytes, err = yaml.Marshal(map[string]string{"a": "1"}) + So(err, ShouldBeNil) + So(yamlBytes, ShouldNotBeEmpty) + err = yaml.Unmarshal(yamlBytes, &uField) + So(err, ShouldNotBeNil) + }) +} + func TestCasbin(t *testing.T) { Convey("test casbin", t, func(c C) { testEnforce := func(e *casbin.Enforcer, sub string, obj interface{}, act string, res bool) { - c.So(e.Enforce(sub, obj, act), ShouldEqual, res) + actual, _ := e.EnforceSafe(sub, obj, act) + c.So(actual, ShouldEqual, res) } cfg := &Config{ Policies: []Policy{ { User: "alice", - Field: "db1.tbl1.col1", - Action: "read", + Field: mustNewField(c, "db1.tbl1.*"), + Action: ReadAction, }, { User: "bob", - Field: "db1.tbl2.col1", - Action: "write", + Field: mustNewField(c, "db1.tbl2.col1"), + Action: WriteAction, }, { User: "data_group_admin", - Field: "field_group", - Action: "write", + Field: mustNewField(c, "db1.tbl1.col2"), + Action: WriteAction, + }, + { + User: "data_group_admin", + Field: mustNewField(c, "db1.tbl2.col1"), + Action: WriteAction, }, }, UserGroup: map[string][]string{ @@ -53,27 +134,27 @@ func TestCasbin(t *testing.T) { "alice", }, }, - FieldGroup: map[string][]string{ - "field_group": { - "db1.tbl1.col1", - "db1.tbl2.col1", - }, - }, } e, err := NewCasbin(cfg) So(err, ShouldBeNil) - testEnforce(e, "alice", "db1.tbl1.col1", "read", true) - testEnforce(e, "alice", "db1.tbl1.col1", "write", true) - testEnforce(e, "alice", "db1.tbl2.col1", "read", false) - testEnforce(e, "alice", "db1.tbl2.col1", "write", true) - testEnforce(e, "bob", "db1.tbl1.col1", "read", false) - testEnforce(e, "bob", "db1.tbl1.col1", "write", false) - testEnforce(e, "bob", "db1.tbl2.col1", "read", false) - testEnforce(e, "bob", "db1.tbl2.col1", "write", true) + testEnforce(e, "alice", "db1.tbl1.col1", ReadAction, true) + testEnforce(e, "alice", "db1.tbl1.col1", WriteAction, false) + testEnforce(e, "alice", "db1.tbl2.col1", ReadAction, true) + testEnforce(e, "alice", "db1.tbl2.col1", WriteAction, true) + testEnforce(e, "alice", "db1.tbl1.col2", ReadAction, true) + testEnforce(e, "alice", "db1.tbl1.col2", WriteAction, true) + testEnforce(e, "alice", "db1.tbl1.col3", ReadAction, true) + testEnforce(e, "alice", "db1.tbl1.col3", WriteAction, false) + testEnforce(e, "bob", "db1.tbl1.col1", ReadAction, false) + testEnforce(e, "bob", "db1.tbl1.col1", WriteAction, false) + testEnforce(e, "bob", "db1.tbl2.col1", ReadAction, true) + testEnforce(e, "bob", "db1.tbl2.col1", WriteAction, true) + testEnforce(e, "bob", "db1", ReadAction, false) + testEnforce(e, "bob", "db1.tbl1.col1", ReadAction, false) }) - Convey("test invalid field config", t, func() { + Convey("test invalid config", t, func(c C) { // nil config _, err := NewCasbin(nil) So(err, ShouldNotBeNil) @@ -84,8 +165,8 @@ func TestCasbin(t *testing.T) { Policies: []Policy{ { User: "user1", - Field: "db1.tbl1.col1", - Action: "read", + Field: mustNewField(c, "db1.tbl1.col1"), + Action: ReadAction, }, }, } @@ -97,8 +178,8 @@ func TestCasbin(t *testing.T) { Policies: []Policy{ { User: "", - Field: "db1.tbl1.col1", - Action: "read", + Field: mustNewField(c, "db1.tbl1.col1"), + Action: ReadAction, }, }, } @@ -107,26 +188,12 @@ func TestCasbin(t *testing.T) { So(err, ShouldNotBeNil) So(errors.Cause(err), ShouldEqual, ErrEmptyConfigItem) - // empty field - cfg = &Config{ - Policies: []Policy{ - { - User: "user1", - Field: "", - Action: "read", - }, - }, - } - _, err = NewCasbin(cfg) - So(err, ShouldNotBeNil) - So(errors.Cause(err), ShouldEqual, ErrEmptyConfigItem) - // empty action cfg = &Config{ Policies: []Policy{ { User: "user1", - Field: "db1.tbl1.col1", + Field: mustNewField(c, "db1.tbl1.col1"), Action: "", }, }, @@ -135,55 +202,13 @@ func TestCasbin(t *testing.T) { So(err, ShouldNotBeNil) So(errors.Cause(err), ShouldEqual, ErrEmptyConfigItem) - // invalid field, not enough field parts - cfg = &Config{ - Policies: []Policy{ - { - User: "user1", - Field: "db1", - Action: "read", - }, - }, - } - _, err = NewCasbin(cfg) - So(err, ShouldNotBeNil) - So(errors.Cause(err), ShouldEqual, ErrInvalidField) - - // invalid field, more than required field parts - cfg = &Config{ - Policies: []Policy{ - { - User: "user1", - Field: "db1.tbl1.col1.what?", - Action: "read", - }, - }, - } - _, err = NewCasbin(cfg) - So(err, ShouldNotBeNil) - So(errors.Cause(err), ShouldEqual, ErrInvalidField) - - // invalid field, contains empty field part - cfg = &Config{ - Policies: []Policy{ - { - User: "user1", - Field: "db1..col1", - Action: "read", - }, - }, - } - _, err = NewCasbin(cfg) - So(err, ShouldNotBeNil) - So(errors.Cause(err), ShouldEqual, ErrInvalidField) - // correct control group with user groups cfg = &Config{ Policies: []Policy{ { User: "a", - Field: "fg1", - Action: "read", + Field: mustNewField(c, "db1.tbl1.col1"), + Action: ReadAction, }, }, UserGroup: map[string][]string{ @@ -194,14 +219,6 @@ func TestCasbin(t *testing.T) { "user1", }, }, - FieldGroup: map[string][]string{ - "fg1": { - "db1.tbl1.col1", - }, - "fg2": { - "db1.tbl1.col1", - }, - }, } _, err = NewCasbin(cfg) So(err, ShouldBeNil) @@ -211,8 +228,8 @@ func TestCasbin(t *testing.T) { Policies: []Policy{ { User: "a", - Field: "fg1", - Action: "read", + Field: mustNewField(c, "db1.tbl1.col1"), + Action: ReadAction, }, }, UserGroup: map[string][]string{ @@ -223,44 +240,6 @@ func TestCasbin(t *testing.T) { "user1", }, }, - FieldGroup: map[string][]string{ - "fg1": { - "db1.tbl1.col1", - }, - "fg2": { - "db1.tbl1.col1", - }, - }, - } - _, err = NewCasbin(cfg) - So(err, ShouldNotBeNil) - So(errors.Cause(err), ShouldEqual, ErrEmptyConfigItem) - - // empty group name in field groups - cfg = &Config{ - Policies: []Policy{ - { - User: "a", - Field: "fg1", - Action: "read", - }, - }, - UserGroup: map[string][]string{ - "a": { - "user1", - }, - "b": { - "user1", - }, - }, - FieldGroup: map[string][]string{ - "fg1": { - "db1.tbl1.col1", - }, - "": { - "db1.tbl1.col1", - }, - }, } _, err = NewCasbin(cfg) So(err, ShouldNotBeNil) @@ -271,8 +250,8 @@ func TestCasbin(t *testing.T) { Policies: []Policy{ { User: "a", - Field: "fg1", - Action: "read", + Field: mustNewField(c, "db1.tbl1.col1"), + Action: ReadAction, }, }, UserGroup: map[string][]string{ @@ -284,110 +263,10 @@ func TestCasbin(t *testing.T) { "user1", }, }, - FieldGroup: map[string][]string{ - "fg1": { - "db1.tbl1.col1", - }, - "fg2": { - "db1.tbl1.col1", - }, - }, - } - _, err = NewCasbin(cfg) - So(err, ShouldNotBeNil) - So(errors.Cause(err), ShouldEqual, ErrEmptyConfigItem) - - // empty field name in field groups - cfg = &Config{ - Policies: []Policy{ - { - User: "a", - Field: "fg1", - Action: "read", - }, - }, - UserGroup: map[string][]string{ - "a": { - "user1", - }, - "b": { - "user1", - }, - }, - FieldGroup: map[string][]string{ - "fg1": { - "db1.tbl1.col1", - "", - }, - "fg2": { - "db1.tbl1.col1", - }, - }, } _, err = NewCasbin(cfg) So(err, ShouldNotBeNil) So(errors.Cause(err), ShouldEqual, ErrEmptyConfigItem) - - // unknown field group - cfg = &Config{ - Policies: []Policy{ - { - User: "a", - Field: "fg3", - Action: "read", - }, - }, - UserGroup: map[string][]string{ - "a": { - "user1", - }, - "b": { - "user1", - }, - }, - FieldGroup: map[string][]string{ - "fg1": { - "db1.tbl1.col1", - }, - "fg2": { - "db1.tbl1.col1", - }, - }, - } - _, err = NewCasbin(cfg) - So(err, ShouldNotBeNil) - So(errors.Cause(err), ShouldEqual, ErrInvalidField) - - // invalid field in field group - cfg = &Config{ - Policies: []Policy{ - { - User: "a", - Field: "fg1", - Action: "read", - }, - }, - UserGroup: map[string][]string{ - "a": { - "user1", - }, - "b": { - "user1", - }, - }, - FieldGroup: map[string][]string{ - "fg1": { - "db1.tbl1.col1", - "db1", - }, - "fg2": { - "db1.tbl1.col1", - }, - }, - } - _, err = NewCasbin(cfg) - So(err, ShouldNotBeNil) - So(errors.Cause(err), ShouldEqual, ErrInvalidField) }) Convey("unsupported features", t, func() { a, err := NewAdapter(&Config{}) @@ -404,3 +283,23 @@ func TestCasbin(t *testing.T) { So(err, ShouldEqual, ErrNotSupported) }) } + +func TestField(t *testing.T) { + Convey("test field", t, func(c C) { + // normal field matches wildcard field + f := mustNewField(c, "db1.tbl1.col1") + So(f.MatchesString("db1.tbl1.col1"), ShouldBeTrue) + So(f.MatchesString("db1.tbl1.c*1"), ShouldBeTrue) + So(f.MatchesString("db*"), ShouldBeFalse) + So(f.MatchesField(nil), ShouldBeFalse) + So((*Field)(nil).MatchesField(&f), ShouldBeFalse) + So(f.MatchesString("db1.tbl2.col1"), ShouldBeFalse) + // reverse that + f = mustNewField(c, "db1.tb*.col1") + So(f.MatchesString("db1.tb1.col1"), ShouldBeTrue) + So(f.MatchesString("db1.tb2.col1"), ShouldBeTrue) + So(f.MatchesString("db2.tb1.col1"), ShouldBeFalse) + So(f.MatchesString("db1.tb*.col1"), ShouldBeTrue) + So(f.MatchesString("db1.*tb.col1"), ShouldBeFalse) + }) +} diff --git a/cmd/cql-secure-gateway/casbin/casbin.go b/cmd/cql-secure-gateway/casbin/casbin.go index 584307a9d..d629f6f79 100644 --- a/cmd/cql-secure-gateway/casbin/casbin.go +++ b/cmd/cql-secure-gateway/casbin/casbin.go @@ -17,12 +17,20 @@ package casbin import ( + "strings" + "github.com/casbin/casbin" "github.com/casbin/casbin/model" "github.com/casbin/casbin/persist" ) -const modelConf = ` +const ( + // ReadAction defines action for read. + ReadAction = "read" + // WriteAction defines action for read, includes read permission when appear in policy config. + WriteAction = "write" + + modelConf = ` [request_definition] r = sub, obj, act @@ -31,13 +39,13 @@ p = sub, obj, act [role_definition] g = _, _ -g2 = _, _ [policy_effect] e = some(where (p.eft == allow)) [matchers] -m = g(r.sub, p.sub) && g2(r.obj, p.obj) && r.act == p.act` +m = g(r.sub, p.sub) && hasPrivilege(r.obj, p.obj, r.act, p.act)` +) // NewCasbin returns a casbin enforcer instance with // fixed rbac_model_with_resource_roles model and custom structured config. @@ -51,5 +59,29 @@ func NewCasbin(cfg *Config) (e *casbin.Enforcer, err error) { return } e = casbin.NewEnforcer(m, rule) + e.AddFunction("hasPrivilege", func(args ...interface{}) (v interface{}, err error) { + var ( + s1 = args[0].(string) + s2 = args[1].(string) + requestAction = args[2].(string) + policyAction = args[3].(string) + f1, f2 *Field + ) + + if !strings.EqualFold(requestAction, policyAction) && + !(strings.EqualFold(requestAction, ReadAction) && strings.EqualFold(policyAction, WriteAction)) { + v = false + return + } + if f1, err = NewFieldFromString(s1); err != nil { + return + } + if f2, err = NewFieldFromString(s2); err != nil { + return + } + + v = f1.MatchesField(f2) + return + }) return } diff --git a/cmd/cql-secure-gateway/casbin/util_test.go b/cmd/cql-secure-gateway/casbin/util_test.go new file mode 100644 index 000000000..e55cd44eb --- /dev/null +++ b/cmd/cql-secure-gateway/casbin/util_test.go @@ -0,0 +1,55 @@ +/* + * Copyright 2018 The CovenantSQL Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package casbin + +import ( + "testing" + + . "github.com/smartystreets/goconvey/convey" +) + +func TestGlobMatch(t *testing.T) { + Convey("glob match test", t, func() { + So(globMatch("a*", "a"), ShouldBeTrue) + So(globMatch("a*", "ab"), ShouldBeTrue) + So(globMatch("a*", "abc"), ShouldBeTrue) + So(globMatch("*a", "a"), ShouldBeTrue) + So(globMatch("*a", "ca"), ShouldBeTrue) + So(globMatch("*a", "bca"), ShouldBeTrue) + So(globMatch("*a*", "a"), ShouldBeTrue) + So(globMatch("*a*", "ab"), ShouldBeTrue) + So(globMatch("*a*", "ca"), ShouldBeTrue) + So(globMatch("*a*", "abc"), ShouldBeTrue) + So(globMatch("*a*", "b"), ShouldBeFalse) + So(globMatch("**a", "a"), ShouldBeTrue) + So(globMatch("a*b", "ab"), ShouldBeTrue) + So(globMatch("a*b", "acb"), ShouldBeTrue) + So(globMatch("a*b", "accb"), ShouldBeTrue) + So(globMatch("a*b", "abc"), ShouldBeFalse) + So(globMatch("ab", "abc"), ShouldBeFalse) + So(globMatch("abc", "bc"), ShouldBeFalse) + }) + Convey("try both way glob", t, func() { + So(tryGlobMatch("a*", "a"), ShouldBeTrue) + So(tryGlobMatch("a", "a*"), ShouldBeTrue) + So(tryGlobMatch("a", "a"), ShouldBeTrue) + So(tryGlobMatch("a*", "a*"), ShouldBeTrue) + So(tryGlobMatch("a*", "b*"), ShouldBeFalse) + So(tryGlobMatch("*a", "a*"), ShouldBeFalse) + So(tryGlobMatch("a*", "A"), ShouldBeTrue) + }) +} diff --git a/cmd/cql-secure-gateway/casbin/utils.go b/cmd/cql-secure-gateway/casbin/utils.go new file mode 100644 index 000000000..4ccc0a8ef --- /dev/null +++ b/cmd/cql-secure-gateway/casbin/utils.go @@ -0,0 +1,88 @@ +/* + * Copyright 2018 The CovenantSQL Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package casbin + +import ( + "strings" +) + +func tryGlobMatch(s1, s2 string) bool { + // only support * globing + s1 = strings.ToLower(s1) + s2 = strings.ToLower(s2) + s1StarIndex := strings.Index(s1, "*") + s2StarIndex := strings.Index(s2, "*") + if s1StarIndex == -1 && s2StarIndex == -1 { + // normal string + return s1 == s2 + } + + if s1StarIndex != -1 && s2StarIndex != -1 { + // both contains stars, overlapping could not be checked + return s1 == s2 + } + + if s1StarIndex == -1 { + // s2 contains a star character + return globMatch(s2, s1) + } else { + return globMatch(s1, s2) + } +} + +func globMatch(pattern, s string) bool { + for len(pattern) > 0 { + var ( + star bool + chunk string + ) + star, chunk, pattern = scanChunk(pattern) + if star && chunk == "" { + return true + } + if strings.HasPrefix(s, chunk) && (s == chunk || len(pattern) > 0) { + s = s[len(chunk):] + continue + } + if star { + if pos := strings.Index(s, chunk); pos != -1 { + t := s[pos+len(chunk):] + if len(pattern) == 0 && len(t) > 0 { + continue + } + s = t + continue + } + } + return false + } + return len(s) == 0 +} + +func scanChunk(pattern string) (star bool, chunk, rest string) { + for len(pattern) > 0 && pattern[0] == '*' { + pattern = pattern[1:] + star = true + } + var i int + for i = 0; i < len(pattern); i++ { + if pattern[i] == '*' { + break + } + } + return star, pattern[0:i], pattern[i:] +} diff --git a/cmd/cql-secure-gateway/config.go b/cmd/cql-secure-gateway/config.go index 3b90e4f30..7e1243f62 100644 --- a/cmd/cql-secure-gateway/config.go +++ b/cmd/cql-secure-gateway/config.go @@ -20,7 +20,7 @@ import ( "io/ioutil" "os" "path" - "strings" + "sync" cw "github.com/CovenantSQL/CovenantSQL/cmd/cql-secure-gateway/casbin" "github.com/CovenantSQL/CovenantSQL/conf" @@ -35,15 +35,17 @@ import ( type encryptionConfig struct { KeyPath string `yaml:"Key"` // key path for encryption config Key *asymmetric.PrivateKey `yaml:"-"` // encryption key - Fields []string `yaml:"Fields"` // encrypted fields config, support field group defines in grants config + Fields []cw.Field `yaml:"Fields"` // encrypted fields config, support field group defines in grants config } type authConfig struct { - Users map[string]string `yaml:"Users"` // user to password mapping - Grants *cw.Config `yaml:"Grants"` // casbin grants config - Enforcer *casbin.Enforcer `yaml:"-"` // casbin enforcer - Encryption []*encryptionConfig `yaml:"Encryption"` // encryption fields settings - FlattenEncryptionConfig map[string]int `yaml:"-"` // field to key offset + encryptionCacheLock sync.RWMutex + Users map[string]string `yaml:"Users"` // user to password mapping + Grants *cw.Config `yaml:"Grants"` // casbin grants config + Enforcer *casbin.Enforcer `yaml:"-"` // casbin enforcer + Encryption []*encryptionConfig `yaml:"Encryption"` // encryption fields settings + EncryptionFieldCache map[string]int `yaml:"-"` // field cache of encryption + WildcardEncryptionFieldCache map[string]int `yaml:"-"` // field cache of wildcard encryption fields } type sgConfig struct { @@ -118,7 +120,11 @@ func (ac *authConfig) buildEncryption() (err error) { } } - ac.FlattenEncryptionConfig = make(map[string]int) + ac.EncryptionFieldCache = map[string]int{} + ac.WildcardEncryptionFieldCache = map[string]int{} + + ac.encryptionCacheLock.Lock() + defer ac.encryptionCacheLock.Unlock() // load and validate encryption config to match with grants for i, ec := range ac.Encryption { @@ -145,28 +151,8 @@ func (ac *authConfig) buildSingleEncryptionConfig(wd string, configIndex int, ec // validate fields for _, f := range ec.Fields { - if ac.Grants != nil && ac.Grants.FieldGroup != nil { - if _, exists := ac.Grants.FieldGroup[f]; exists { - // exists in field group - // flatten - - for _, sf := range ac.Grants.FieldGroup[f] { - if err = ac.setFieldEncryptionKey(sf, configIndex); err != nil { - return - } - } - - continue - } - } - - // validate as normal .
. field - if err = ac.validateField(f); err != nil { - return - } - // set to flatten mapping - if err = ac.setFieldEncryptionKey(f, configIndex); err != nil { + if err = ac.checkAndBuildEncryptionCache(&f, configIndex); err != nil { return } } @@ -174,50 +160,95 @@ func (ac *authConfig) buildSingleEncryptionConfig(wd string, configIndex int, ec return } -func (ac *authConfig) setFieldEncryptionKey(f string, ecOffset int) (err error) { +func (ac *authConfig) checkAndBuildEncryptionCache(f *cw.Field, ecOffset int) (err error) { // check field encryption configuration over-lapping // iterate existing setting - for p, o := range ac.FlattenEncryptionConfig { - var m bool - if m, err = fieldMatches(p, f); err != nil { - return + for field, o := range ac.EncryptionFieldCache { + if f.MatchesString(field) { + if o != ecOffset { + err = errors.Wrapf(ErrFieldEncryption, + "duplicated encryption assigned to column (previous: %s -> %s, current: %s -> %s)", + field, ac.Encryption[o].KeyPath, + f.String(), ac.Encryption[ecOffset].KeyPath, + ) + return + } } + } - if m && o != ecOffset { - err = errors.Wrapf(ErrFieldEncryption, - "duplicated encryption assigned to column (previous: %s -> %s, current: %s -> %s)", - p, ac.Encryption[o].KeyPath, - f, ac.Encryption[ecOffset].KeyPath, - ) - return + if !f.IsWildcard() { + for field, o := range ac.WildcardEncryptionFieldCache { + if f.MatchesString(field) { + if o != ecOffset { + err = errors.Wrapf(ErrFieldEncryption, + "duplicated encryption assigned to column (previous: %s -> %s, current: %s -> %s)", + field, ac.Encryption[o].KeyPath, + f.String(), ac.Encryption[ecOffset].KeyPath, + ) + return + } + } } - } - // set new record - ac.FlattenEncryptionConfig[f] = ecOffset + // set + ac.EncryptionFieldCache[f.String()] = ecOffset + } else { + ac.WildcardEncryptionFieldCache[f.String()] = ecOffset + } return } -func (ac *authConfig) validateField(f string) (err error) { - parts := strings.Split(f, ".") - - if len(parts) != 3 { - err = errors.Wrapf(ErrInvalidField, - "invalid field: %s, should be in form \".
.\", wildcard is supported", f) +// GetEncryptionConfig returns encryption config for specified field. +func (ac *authConfig) GetEncryptionConfig(f *cw.Field) (ecConfig *encryptionConfig, err error) { + if f == nil { + return + } + if f.IsWildcard() { + err = errors.Wrap(ErrInvalidField, "could not get wildcard field encryption key") return } - for _, p := range parts { - p = strings.TrimSpace(p) + var ( + fieldStr = f.String() + ecOffset = -1 + shouldFillCache bool + ) - if p == "" { - err = errors.Wrapf(ErrInvalidField, - "invalid field: %s, /
/ parts should not be blanked", f) + func() { + ac.encryptionCacheLock.RLock() + defer ac.encryptionCacheLock.RUnlock() + if o, exists := ac.EncryptionFieldCache[fieldStr]; exists { + // hit + ecOffset = o return } + for wf, o := range ac.WildcardEncryptionFieldCache { + if f.MatchesString(wf) { + // hit + ecOffset = o + shouldFillCache = true + return + } + } + // plain field + shouldFillCache = true + }() + + if shouldFillCache { + func() { + ac.encryptionCacheLock.Lock() + defer ac.encryptionCacheLock.Unlock() + + ac.EncryptionFieldCache[fieldStr] = ecOffset + }() + } + + if ecOffset < 0 { + return } + ecConfig = ac.Encryption[ecOffset] return } diff --git a/cmd/cql-secure-gateway/config_test.go b/cmd/cql-secure-gateway/config_test.go index 26a88638f..8a8c43bd7 100644 --- a/cmd/cql-secure-gateway/config_test.go +++ b/cmd/cql-secure-gateway/config_test.go @@ -48,7 +48,7 @@ SecureGateway: Field: db1.tbl1.col1 Action: write - User: read_group - Field: tbl2_fields + Field: db1.tbl2.col* Action: read - User: admin_group Field: db1.tbl3.col* @@ -61,13 +61,10 @@ SecureGateway: - user1 - user2 - user3 - FieldGroups: - tbl2_fields: - - db1.tbl2.col* Encryption: - Key: key1.key Fields: - - tbl2_fields + - db1.tbl2.col* - Key: key2.key Fields: - db1.tbl3.col* @@ -91,7 +88,7 @@ SecureGateway: Field: db1.tbl1.col1 Action: write - User: read_group - Field: tbl2_fields + Field: db1.tbl2.col* Action: read - User: admin_group Field: db1.tbl3.col* @@ -104,13 +101,10 @@ SecureGateway: - user1 - user2 - user3 - FieldGroups: - tbl2_fields: - - db1.tbl2.col* Encryption: - Key: key1.key Fields: - - tbl2_fields + - db1.tbl2.col* - Key: key2.key Fields: - db1.tbl3.col* @@ -136,7 +130,7 @@ SecureGateway: Field: db1.tbl1.col1 Action: write - User: read_group - Field: tbl2_fields + Field: db1.tbl2.col* Action: read - User: admin_group Field: db1.tbl3.col* @@ -149,13 +143,10 @@ SecureGateway: - user1 - user2 - user3 - FieldGroups: - tbl2_fields: - - db1.tbl2.col* Encryption: - Key: key1.key Fields: - - tbl2_fields + - db1.tbl2.col* - Key: key2.key Fields: - db1.tbl3.col* @@ -177,7 +168,7 @@ SecureGateway: Field: db1.tbl1.col1 Action: write - User: read_group - Field: tbl2_fields + Field: db1.tbl2.col* Action: read - User: admin_group Field: db1.tbl3.col* @@ -190,13 +181,10 @@ SecureGateway: - user1 - user2 - user3 - FieldGroups: - tbl2_fields: - - db1.tbl2.col* Encryption: - Key: key1.key Fields: - - tbl2_fields + - db1.tbl2.col* - db1.tbl3.col1 - Key: key2.key Fields: @@ -219,7 +207,7 @@ SecureGateway: Field: db1.tbl1.col1 Action: write - User: read_group - Field: tbl2_fields + Field: db1.tbl2.col* Action: read - User: admin_group Field: db1.tbl3.col* @@ -232,16 +220,13 @@ SecureGateway: - user1 - user2 - user3 - FieldGroups: - tbl2_fields: - - db1.tbl2.col* Encryption: - Key: key2.key Fields: - db1.tbl2.col2 - Key: key1.key Fields: - - tbl2_fields + - db1.tbl2.col* ` invalidEncryptionField = ` SecureGateway: @@ -260,7 +245,7 @@ SecureGateway: Field: db1.tbl1.col1 Action: write - User: read_group - Field: tbl2_fields + Field: db1.tbl2.col* Action: read - User: admin_group Field: db1.tbl3.col* @@ -273,13 +258,10 @@ SecureGateway: - user1 - user2 - user3 - FieldGroups: - tbl2_fields: - - db1.tbl2.col* Encryption: - Key: key1.key Fields: - - tbl2_fields + - db1.tbl2.col* - Key: key2.key Fields: - db1.tbl3.col* @@ -302,7 +284,7 @@ SecureGateway: Field: db1.tbl1.col1 Action: write - User: read_group - Field: tbl2_fields + Field: db1.tbl2.col* Action: read - User: admin_group Field: db1.tbl3.col* @@ -315,13 +297,10 @@ SecureGateway: - user1 - user2 - user3 - FieldGroups: - tbl2_fields: - - db1.tbl2.col* Encryption: - Key: key1.key Fields: - - tbl2_fields + - db1.tbl2.col* - Key: key2.key Fields: - db1.tbl3.col* @@ -344,7 +323,7 @@ SecureGateway: Field: db1.tbl1.col1 Action: write - User: read_group - Field: tbl2_fields + Field: db1.tbl2.col* Action: read - User: admin_group Field: db1.tbl3.col* @@ -357,13 +336,10 @@ SecureGateway: - user1 - user2 - user3 - FieldGroups: - tbl2_fields: - - db1.tbl2.col* Encryption: - Key: key1.key Fields: - - tbl2_fields + - db1.tbl2.col* - Key: key2.key Fields: - db1.tbl3.col* diff --git a/cmd/cql-secure-gateway/errors.go b/cmd/cql-secure-gateway/errors.go index 158414ad8..774087377 100644 --- a/cmd/cql-secure-gateway/errors.go +++ b/cmd/cql-secure-gateway/errors.go @@ -28,4 +28,14 @@ var ( ErrFieldEncryption = errors.New("invalid field encryption config") // ErrInvalidField defines invalid field format. ErrInvalidField = cw.ErrInvalidField + // ErrUnauthorizedQuery defines error for unauthorized query. + ErrUnauthorizedQuery = errors.New("unauthorized query") + // ErrUnsupportedEncryptionFieldQuery defines error for unsupported encryption field query. + ErrUnsupportedEncryptionFieldQuery = errors.New("unsupported encryption field query") + // ErrNilPointer defines error for nil output. + ErrNilPointer = errors.New("destination pointer is nil") + // ErrNotPointer defines error for scanning to non pointer type. + ErrNotPointer = errors.New("destination not a pointer") + // ErrUnsupportedType defines invalid type to encrypt/decrypt. + ErrUnsupportedType = errors.New("unsupported type for encryption") ) diff --git a/cmd/cql-secure-gateway/handler.go b/cmd/cql-secure-gateway/handler.go new file mode 100644 index 000000000..f7f2af85f --- /dev/null +++ b/cmd/cql-secure-gateway/handler.go @@ -0,0 +1,639 @@ +/* + * Copyright 2018 The CovenantSQL Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package main + +import ( + "database/sql" + "database/sql/driver" + "fmt" + "reflect" + "strconv" + "strings" + + "github.com/CovenantSQL/CovenantSQL/cmd/cql-mysql-adapter/cursor" + "github.com/CovenantSQL/CovenantSQL/cmd/cql-mysql-adapter/handler" + cw "github.com/CovenantSQL/CovenantSQL/cmd/cql-secure-gateway/casbin" + "github.com/CovenantSQL/CovenantSQL/crypto" + "github.com/CovenantSQL/sqlparser" + "github.com/pkg/errors" +) + +type autoDecryptWrapper struct { + rows cursor.Rows + decryptConfig map[int]*encryptionConfig +} + +func (w *autoDecryptWrapper) Close() error { + if w.rows != nil { + return w.Close() + } + return nil +} + +func (w *autoDecryptWrapper) Columns() ([]string, error) { + if w.rows != nil { + return w.Columns() + } + return nil, nil +} + +func (w *autoDecryptWrapper) Err() error { + if w.rows != nil { + return w.Err() + } + return nil +} + +func (w *autoDecryptWrapper) Next() bool { + if w.rows != nil { + return w.Next() + } + + return false +} + +func (w *autoDecryptWrapper) Scan(dest ...interface{}) (err error) { + if w.rows != nil { + // build decrypt array + var ( + decryptArray = make([]interface{}, len(dest)) + v *encryptionConfig + exists bool + ) + + for i := range dest { + if v, exists = w.decryptConfig[i]; exists && v != nil { + byteArray := make([]byte, 0) + decryptArray[i] = &byteArray + } else { + decryptArray[i] = dest[i] + } + } + + if err = w.rows.Scan(decryptArray...); err != nil { + return + } + + // decrypt + for i, v := range w.decryptConfig { + if v == nil { + continue + } + + encryptedDataPtr, _ := decryptArray[i].(*[]byte) + + if encryptedDataPtr == nil || len(*encryptedDataPtr) == 0 { + // nil value + if err = w.convertAssign(dest[i], nil); err != nil { + return + } + } + + var ( + result []byte + encryptedData = *encryptedDataPtr + ) + if result, err = crypto.DecryptAndCheck(v.Key, encryptedData); err != nil { + return + } + + if err = w.convertAssign(dest[i], result); err != nil { + return + } + } + } + + return +} + +func (w *autoDecryptWrapper) convertAssign(dest interface{}, src []byte) (err error) { + switch d := dest.(type) { + case *string: + if d == nil { + return ErrNilPointer + } + *d = string(src) + return nil + + case *interface{}: + if d == nil { + return ErrNilPointer + } + *d = w.cloneBytes(src) + return nil + case *[]byte: + if d == nil { + return ErrNilPointer + } + *d = w.cloneBytes(src) + case *sql.RawBytes: + if d == nil { + return ErrNilPointer + } + *d = src + return nil + case *bool: + var bv driver.Value + if bv, err = driver.Bool.ConvertValue(src); err == nil { + *d = bv.(bool) + } + return + } + + if scanner, ok := dest.(sql.Scanner); ok { + return scanner.Scan(src) + } + + dpv := reflect.ValueOf(dest) + if dpv.Kind() != reflect.Ptr { + return ErrNotPointer + } + if dpv.IsNil() { + return ErrNilPointer + } + sv := reflect.ValueOf(src) + + dv := reflect.Indirect(dpv) + if sv.IsValid() && sv.Type().AssignableTo(dv.Type()) { + dv.Set(reflect.ValueOf(w.cloneBytes(src))) + return nil + } + if dv.Kind() == sv.Kind() && sv.Type().ConvertibleTo(dv.Type()) { + dv.Set(sv.Convert(dv.Type())) + return nil + } + s := string(src) + switch dv.Kind() { + case reflect.Ptr: + if src == nil { + dv.Set(reflect.Zero(dv.Type())) + return nil + } + dv.Set(reflect.New(dv.Type().Elem())) + return w.convertAssign(dv.Interface(), src) + case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: + var i64 int64 + if i64, err = strconv.ParseInt(s, 10, dv.Type().Bits()); err != nil { + return + } + dv.SetInt(i64) + return nil + case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64: + var u64 uint64 + if u64, err = strconv.ParseUint(s, 10, dv.Type().Bits()); err != nil { + return + } + dv.SetUint(u64) + return nil + case reflect.Float32, reflect.Float64: + var f64 float64 + if f64, err = strconv.ParseFloat(s, dv.Type().Bits()); err != nil { + return + } + dv.SetFloat(f64) + return nil + case reflect.String: + dv.SetString(s) + return nil + } + + return ErrUnsupportedType +} + +func (w *autoDecryptWrapper) cloneBytes(b []byte) []byte { + if b == nil { + return nil + } + c := make([]byte, len(b)) + copy(c, b) + return c +} + +// Handler override the default mysql-adapter handler logic and added query privilege logic. +type Handler struct { + *handler.Handler + r *Resolver + authConfig *authConfig +} + +// NewHandler returns the secure gateway handler object. +func NewHandler(authConfig *authConfig) (h *Handler) { + var r = NewResolver() + return &Handler{ + Handler: handler.NewHandlerWithResolver(r.Resolver), + r: r, + authConfig: authConfig, + } +} + +// Resolve resolves the string query to handler query. +func (h *Handler) Resolve(user string, dbID string, query string) (q cursor.Query, err error) { + if err = h.EnsureDatabase(dbID); err != nil { + return + } + if q, err = h.r.ResolveSingleQuery(dbID, query); err != nil { + return + } + + // further check with privilege and encryption + err = h.ensurePrivilege(user, q) + + return +} + +// Query executes the resolved read query with auto fields decryption. +func (h *Handler) Query(q cursor.Query, args ...interface{}) (rows cursor.Rows, err error) { + if !q.IsRead() { + err = errors.Wrap(ErrQueryLogicError, "not a read query") + return + } + // execute with auto fields decryption on result rows + var origRows cursor.Rows + if origRows, err = h.QueryString(q.GetDatabase(), q.GetQuery(), args...); err != nil { + return + } + + // decrypt args + query := q.(*Query) + rows = &autoDecryptWrapper{ + rows: origRows, + decryptConfig: query.DecryptConfig, + } + return +} + +// Exec executes the resolved write query with auto fields encryption. +func (h *Handler) Exec(q cursor.Query, args ...interface{}) (res sql.Result, err error) { + if q.IsRead() { + err = errors.Wrap(ErrQueryLogicError, "not a write query") + return + } + if q.IsDDL() { + defer h.r.ReloadMeta() + } + query := q.(*Query) + // execute with auto fields encryption + for i, v := range query.EncryptConfig { + if v == nil { + continue + } + // convert argument to byte array + if len(args) >= i { + // not enough arguments + err = errors.Wrapf(ErrQueryLogicError, "not enough arguments, argument %d should be provided", i) + return + } + + if args[i] == nil { + // nil type, not need for encryption + continue + } + + // mysql types will be converted to int/float numeric or byte array + var ( + rawData []byte + ok bool + ) + if rawData, ok = args[i].([]byte); !ok { + // not byte type, convert to string representation + sv := reflect.ValueOf(args[i]) + + switch sv.Kind() { + case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: + rawData = []byte(strconv.FormatInt(sv.Int(), sv.Type().Bits())) + case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64: + rawData = []byte(strconv.FormatUint(sv.Uint(), sv.Type().Bits())) + case reflect.Float32, reflect.Float64: + rawData = []byte(strconv.FormatFloat(sv.Float(), 'f', -1, sv.Type().Bits())) + default: + err = ErrUnsupportedType + return + } + } + + if rawData == nil { + // set argument to nil + args[i] = nil + continue + } + + if args[i], err = crypto.EncryptAndSign(v.Key.PubKey(), rawData); err != nil { + return + } + } + + // send query + return h.ExecString(q.GetDatabase(), q.GetQuery(), args...) +} + +func (h *Handler) ensurePrivilege(user string, rawQuery cursor.Query) (err error) { + var ( + query *Query + ok bool + act string + f *cw.Field + hasAuth bool + encConfig *encryptionConfig + encryptedFields = map[string]*encryptionConfig{} + encryptUnsupportedOps = map[string]bool{} + unauthorizedMessages []string + encryptionMessages []string + ) + if query, ok = rawQuery.(*Query); !ok { + err = errors.Wrap(ErrQueryLogicError, "not a valid query") + return + } + + if query.IsShow() { + return + } + + if query.IsDDL() || query.IsExplain() { + err = errors.Wrap(ErrUnauthorizedQuery, "ddl or explain query is not permitted") + return + } + + // check authority and encryption setting + for _, cr := range query.PhysicalColumnRely { + // build field + if f, err = cw.NewField(query.GetDatabase(), cr.TableName, cr.ColName); err != nil { + err = errors.Wrap(err, "not a valid field") + return + } + + // get encryption config + if encConfig, err = h.authConfig.GetEncryptionConfig(f); err != nil { + err = errors.Wrapf(err, "check field encryption failed") + return + } + + encryptedFields[f.String()] = encConfig + + // role base access control + act = cw.ReadAction + encryptUnsupportedOps = map[string]bool{} + + OperationVisitLoop: + for _, ops := range cr.Ops { + for _, op := range ops { + if encConfig != nil { + // has encryption config + switch op { + case AliasOp, CaseOp, SubQueryOp, DeleteOp, AssignmentOp: + default: + encryptUnsupportedOps[string(op)] = true + } + } + + if op == AssignmentOp || op == DeleteOp { + // contains write operation + act = cw.WriteAction + + // not encrypted field + if encConfig == nil { + break OperationVisitLoop + } + } + } + } + + if hasAuth, err = h.authConfig.Enforcer.EnforceSafe(user, f.String(), act); err != nil { + err = errors.Wrapf(err, "check auth failure") + return + } + + if !hasAuth { + unauthorizedMessages = append(unauthorizedMessages, fmt.Sprintf("%s:%s", f.String(), act)) + } + + if len(encryptUnsupportedOps) != 0 { + ops := make([]string, 0, len(encryptUnsupportedOps)) + for op := range encryptUnsupportedOps { + ops = append(ops, op) + } + encryptionMessages = append(encryptionMessages, + fmt.Sprintf("%s:%s", f.String(), strings.Join(ops, "|"))) + } + } + + if len(unauthorizedMessages) != 0 || !hasAuth { + err = errors.Wrapf(ErrUnauthorizedQuery, "fields: %s", strings.Join(unauthorizedMessages, ",")) + return + } + if len(encryptionMessages) != 0 { + err = errors.Wrapf(ErrUnsupportedEncryptionFieldQuery, + "fields: %s", strings.Join(encryptionMessages, ",")) + return + } + + // check encryption + if query.IsRead() { + // check fields to decrypt, analyzed result columns + for i, c := range query.PhysicalColumnTransformations { + // get physical columns to decrypt + var ( + pc *Column + encConfig *encryptionConfig + ) + if encConfig, pc, err = h.resolveResultColumnDecryptKey(c, query, encryptedFields); err != nil { + tb := sqlparser.NewTrackedBuffer(nil) + err = errors.Wrapf(err, "could not resolve column to decrypt: %s", tb.WriteNode(&c.ColName).String()) + return + } + + if pc != nil { + query.DecryptConfig[i] = encConfig + } + } + } else { + // check field binding parameters + err = h.walkColumns(func(col *Column) (kontinue bool, err error) { + if col == nil || col.Computation == nil { + return false, nil + } else if col.Computation.Op != AssignmentOp { + return true, nil + } + + // process assignment + if len(col.Computation.Operands) != 2 { + tb := sqlparser.NewTrackedBuffer(nil) + err = errors.Wrapf(ErrQueryLogicError, + "should provide bind parameters for encrypted field: %s", tb.WriteNode(&col.ColName).String()) + return + } + + // first operand should be a physical column + if !col.Computation.Operands[0].IsPhysical { + tb := sqlparser.NewTrackedBuffer(nil) + err = errors.Wrapf(ErrQueryLogicError, + "query not supported for encryption", tb.WriteNode(&col.ColName).String()) + return + } + + // second operand should be a bind parameter value + if col.Computation.Operands[1].Computation == nil || col.Computation.Operands[1].Computation.Op != ParameterOp { + tb := sqlparser.NewTrackedBuffer(nil) + err = errors.Wrapf(ErrQueryLogicError, + "query not supported for encryption", tb.WriteNode(&col.ColName).String()) + return + } + + var ( + paramVal *sqlparser.SQLVal + ok bool + ) + + if paramVal, ok = col.Computation.Operands[1].Computation.Data.(*sqlparser.SQLVal); !ok || + paramVal.Type != sqlparser.PosArg && paramVal.Type != sqlparser.ValArg { + tb := sqlparser.NewTrackedBuffer(nil) + err = errors.Wrapf(ErrQueryLogicError, + "column assigned value not supported for encryption", tb.WriteNode(&col.ColName).String()) + return + } + if paramVal.Type == sqlparser.ValArg { + tb := sqlparser.NewTrackedBuffer(nil) + err = errors.Wrapf(ErrQueryLogicError, + "column assigned name argument temporary not supported for encryption", tb.WriteNode(&col.ColName).String()) + return + } + + // find column encryption config + var ( + f *cw.Field + paramKey = string(paramVal.Val) + paramPos int + ) + + // convert parameter key to parameter position + if len(paramKey) < 2 { + tb := sqlparser.NewTrackedBuffer(nil) + err = errors.Wrapf(ErrQueryLogicError, + "invalid column value assignment", tb.WriteNode(&col.ColName).String()) + return + } + if paramPos, err = strconv.Atoi(paramKey[2:]); err != nil { + tb := sqlparser.NewTrackedBuffer(nil) + err = errors.Wrapf(err, "resolve parameter offset for column %s failed", tb.WriteNode(&col.ColName).String()) + return + } + if f, err = cw.NewField(query.GetDatabase(), col.ColName.Qualifier.Name.String(), col.ColName.Name.String()); err != nil { + tb := sqlparser.NewTrackedBuffer(nil) + err = errors.Wrapf(err, "could not resolve column to encrypt: %s", tb.WriteNode(&col.ColName).String()) + return + } + + var exists bool + if query.EncryptConfig[paramPos], exists = encryptedFields[f.String()]; !exists { + tb := sqlparser.NewTrackedBuffer(nil) + err = errors.Wrapf(err, "could not resolve column encryption config: %s", tb.WriteNode(&col.ColName).String()) + return + } + + return false, nil + }, query.PhysicalColumnTransformations) + if err != nil { + err = errors.Wrap(err, "could not resolve column for encryption") + return + } + } + + return +} + +func (h *Handler) resolveResultColumnDecryptKey(c *Column, query *Query, encryptionKeys map[string]*encryptionConfig) (encConfig *encryptionConfig, pc *Column, err error) { + var ( + pcs = NewColumns() + encConfigs []*encryptionConfig + visit func(col *Column) (kontinue bool, err error) + ) + + visit = func(col *Column) (kontinue bool, err error) { + if col.Computation == nil { + if col.IsPhysical { + // check if the column is encrypted column + var ( + f *cw.Field + tempEncConfig *encryptionConfig + exists bool + ) + if f, err = cw.NewField(query.GetDatabase(), col.ColName.Qualifier.Name.String(), + col.ColName.Name.String()); err != nil { + tb := sqlparser.NewTrackedBuffer(nil) + err = errors.Wrapf(err, "could not resolve column to decrypt: %s", tb.WriteNode(&col.ColName).String()) + return + } + + if tempEncConfig, exists = encryptionKeys[f.String()]; exists { + encConfigs = append(encConfigs, tempEncConfig) + pcs = append(pcs, col) + } + } + return false, nil + } + + if col.Computation.Op == FilterOp { + if len(col.Computation.Operands) > 0 { + // first column is the select expression + if err = h.walkColumns(visit, NewColumns(col.Computation.Operands[0])); err != nil { + return + } + } + + return false, nil + } + + return true, nil + } + + if err = h.walkColumns(visit, NewColumns(c)); err != nil { + return + } + + if len(pcs) > 1 { + // multiple physical columns detected + err = errors.Wrapf(ErrQueryLogicError, "multiple encrypted columns related to result column") + return + } + + if len(pcs) == 0 { + return + } + + encConfig = encConfigs[0] + pc = pcs[0] + + return +} + +func (h *Handler) walkColumns(visit func(col *Column) (kontinue bool, err error), cols Columns) (err error) { + for _, c := range cols { + if c == nil { + continue + } + var kontinue bool + if kontinue, err = visit(c); err != nil { + return + } else if kontinue { + if c.Computation != nil && c.Computation.Operands != nil { + if err = h.walkColumns(visit, c.Computation.Operands); err != nil { + return + } + } + } + } + return nil +} diff --git a/cmd/cql-secure-gateway/resolver.go b/cmd/cql-secure-gateway/resolver.go index d21315641..d5f4c3205 100644 --- a/cmd/cql-secure-gateway/resolver.go +++ b/cmd/cql-secure-gateway/resolver.go @@ -78,6 +78,8 @@ const ( JoinUsingOp Op = "JOIN_USING" // AssignmentOp defines the assignment operation of insert/update column/values. AssignmentOp Op = "ASSIGNMENT" + // DeleteOp defines the delete operation of fields. + DeleteOp Op = "DELETE" ) // Op defines the operation type. @@ -91,7 +93,10 @@ type Resolver struct { // Query defines the resolved query of the secure gateway. type Query struct { *resolver.Query - PhysicalColumnRely []*ColumnResult + PhysicalColumnTransformations Columns + PhysicalColumnRely []*ColumnResult + DecryptConfig map[int]*encryptionConfig + EncryptConfig map[int]*encryptionConfig } // Columns defines the column array for resolving. @@ -177,6 +182,13 @@ type ColumnResult struct { Ops [][]Op } +// NewResolver returns the new resolver object. +func NewResolver() *Resolver { + return &Resolver{ + Resolver: resolver.NewResolver(), + } +} + func (r *Resolver) buildExpression(dbID string, expr sqlparser.Expr, originColumns Columns) (cols Columns, err error) { if expr == nil { return @@ -1015,15 +1027,20 @@ func (r *Resolver) buildSelectStatement(dbID string, stmt sqlparser.SelectStatem // GetDependentPhysicalColumns returns the physical table dependency column result. func (r *Resolver) GetDependentPhysicalColumns(dbID string, stmt sqlparser.Statement) (columns []*ColumnResult, err error) { + var cols Columns + if cols, err = r.BuildPhysicalColumnsTransformations(dbID, stmt); err != nil { + return + } + return r.BuildAggregatedPhysicalColumnsTransformations(dbID, cols) +} + +// BuildAggregatedPhysicalColumnsTransformations returns the aggregated physical column related transformations. +func (r *Resolver) BuildAggregatedPhysicalColumnsTransformations(dbID string, cols Columns) (columns []*ColumnResult, err error) { var ( - cols Columns columnMap = make(map[string]map[string]int) visit func(opStack []Op, cols Columns) ) columns = make([]*ColumnResult, 0) - if cols, err = r.BuildPhysicalColumnsTransformations(dbID, stmt); err != nil { - return - } visit = func(opStack []Op, cols Columns) { for _, c := range cols { if !c.IsPhysical { @@ -1059,7 +1076,7 @@ func (r *Resolver) GetDependentPhysicalColumns(dbID string, stmt sqlparser.State return } -// BuildPhysicalColumnsTransformations returns the physical column dependency tree. +// BuildPhysicalColumnsTransformations returns the physical column transformation tree. func (r *Resolver) BuildPhysicalColumnsTransformations(dbID string, stmt sqlparser.Statement) (cols Columns, err error) { var ( tempCols Columns @@ -1139,6 +1156,16 @@ func (r *Resolver) BuildPhysicalColumnsTransformations(dbID string, stmt sqlpars return } cols = append(cols, tempCols...) + for _, c := range originCols { + cols = append(cols, &Column{ + ColName: c.ColName, + IsPhysical: false, + Computation: &Computation{ + Op: DeleteOp, + Operands: NewColumns(c), + }, + }) + } case *sqlparser.DDL: // requires table privilege, if create table statement is passed, requires extra privilege case *sqlparser.Show: @@ -1155,24 +1182,18 @@ func (r *Resolver) BuildPhysicalColumnsTransformations(dbID string, stmt sqlpars // ResolveQuery parses string query and returns multiple query resolver result. func (r *Resolver) ResolveQuery(dbID string, query string) (q []*Query, err error) { - var ( - queries []*resolver.Query - colResult []*ColumnResult - ) + var queries []*resolver.Query if queries, err = r.Resolver.ResolveQuery(dbID, query); err != nil { return } for _, query := range queries { - if colResult, err = r.GetDependentPhysicalColumns(dbID, query.Stmt); err != nil { + newQuery := &Query{Query: query} + if err = r.buildResolveResult(dbID, newQuery); err != nil { return } - - q = append(q, &Query{ - Query: query, - PhysicalColumnRely: colResult, - }) + q = append(q, newQuery) } return @@ -1180,56 +1201,30 @@ func (r *Resolver) ResolveQuery(dbID string, query string) (q []*Query, err erro // ResolveSingleQuery parse string query and make sure there is only one query in the query string. func (r *Resolver) ResolveSingleQuery(dbID string, queryStr string) (q *Query, err error) { - var ( - query *resolver.Query - colResult []*ColumnResult - ) - - if query, err = r.Resolver.ResolveSingleQuery(dbID, queryStr); err != nil { - return - } - - if colResult, err = r.GetDependentPhysicalColumns(dbID, query.Stmt); err != nil { + q = &Query{} + if q.Query, err = r.Resolver.ResolveSingleQuery(dbID, queryStr); err != nil { return } - - q = &Query{ - Query: query, - PhysicalColumnRely: colResult, - } + err = r.buildResolveResult(dbID, q) return } // Resolve process sqlparser statement and returns resolved result. func (r *Resolver) Resolve(dbID string, stmt sqlparser.Statement) (q *Query, err error) { - var ( - query *resolver.Query - colResult []*ColumnResult - ) + q = &Query{} - if query, err = r.Resolver.Resolve(dbID, stmt); err != nil { + if q.Query, err = r.Resolver.Resolve(dbID, stmt); err != nil { return } - - if colResult, err = r.GetDependentPhysicalColumns(dbID, stmt); err != nil { - return - } - - q = &Query{ - Query: query, - PhysicalColumnRely: colResult, - } + err = r.buildResolveResult(dbID, q) return } -// IsShowOrExplain returns if the query is show or explain statement. -func (q *Query) IsShowOrExplain() bool { - if q.Stmt != nil { - switch q.Stmt.(type) { - case *sqlparser.Show, *sqlparser.Explain: - return true - } +func (r *Resolver) buildResolveResult(dbID string, q *Query) (err error) { + if q.PhysicalColumnTransformations, err = r.BuildPhysicalColumnsTransformations(dbID, q.Stmt); err != nil { + return } + q.PhysicalColumnRely, err = r.BuildAggregatedPhysicalColumnsTransformations(dbID, q.PhysicalColumnTransformations) - return false + return } diff --git a/cmd/cql-secure-gateway/server.go b/cmd/cql-secure-gateway/server.go index 76364f6e0..37c601ded 100644 --- a/cmd/cql-secure-gateway/server.go +++ b/cmd/cql-secure-gateway/server.go @@ -19,6 +19,7 @@ package main import ( "net" + "github.com/CovenantSQL/CovenantSQL/cmd/cql-mysql-adapter/cursor" "github.com/CovenantSQL/CovenantSQL/utils/log" mys "github.com/siddontang/go-mysql/server" ) @@ -27,12 +28,14 @@ import ( type Server struct { listener net.Listener cfg *sgConfig + h *Handler } // NewServer bind the service port and return a runnable adapter. func NewServer(sc *sgConfig) (s *Server, err error) { s = &Server{ cfg: sc, + h: NewHandler(sc.AuthConfig), } if s.listener, err = net.Listen("tcp", sc.ListenAddr); err != nil { @@ -55,7 +58,7 @@ func (s *Server) Serve() { } func (s *Server) handleConn(conn net.Conn) { - cur := NewCursor() + cur := cursor.NewCursor(s.h) h, err := mys.NewConnWithUsers(conn, s.cfg.AuthConfig.Users, cur) if err != nil { @@ -77,4 +80,5 @@ func (s *Server) handleConn(conn net.Conn) { // Shutdown ends the server. func (s *Server) Shutdown() { s.listener.Close() + s.h.Close() } From 3f7c57862c8582c3e9741cf9c83b0d56cf82b6af Mon Sep 17 00:00:00 2001 From: auxten Date: Sat, 1 Jun 2019 14:31:14 +0800 Subject: [PATCH 20/24] Add cql-secure-gateway in makefile --- Makefile | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/Makefile b/Makefile index e594c5183..e2b5d1a9e 100644 --- a/Makefile +++ b/Makefile @@ -165,6 +165,12 @@ bin/cql-fuse: -o bin/cql-fuse \ github.com/CovenantSQL/CovenantSQL/cmd/cql-fuse +bin/cql-secure-gateway: + $(GOBUILD) \ + -ldflags "$(ldflags_role_client)" \ + -o bin/cql-secure-gateway \ + github.com/CovenantSQL/CovenantSQL/cmd/cql-secure-gateway + bin/cql-mysql-adapter: $(GOBUILD) \ -ldflags "$(ldflags_role_client)" \ From 80f52c2d218fcd1f5ff3fcc5bdaa2e3ece73a7a2 Mon Sep 17 00:00:00 2001 From: auxten Date: Thu, 6 Jun 2019 10:12:38 +0800 Subject: [PATCH 21/24] Fix various bugs in sg --- cmd/cql-mysql-adapter/cursor/cursor.go | 3 + cmd/cql-mysql-adapter/resolver/resolver.go | 2 +- cmd/cql-secure-gateway/handler.go | 203 +++++++++++---------- cmd/cql-secure-gateway/main.go | 1 + cmd/cql-secure-gateway/resolver.go | 19 +- 5 files changed, 122 insertions(+), 106 deletions(-) diff --git a/cmd/cql-mysql-adapter/cursor/cursor.go b/cmd/cql-mysql-adapter/cursor/cursor.go index 2f68c071e..8ac64afd8 100644 --- a/cmd/cql-mysql-adapter/cursor/cursor.go +++ b/cmd/cql-mysql-adapter/cursor/cursor.go @@ -266,6 +266,9 @@ func (c *Cursor) UseDB(dbName string) (err error) { } err = c.h.EnsureDatabase(dbName) + if err == nil { + c.curDB = dbName + } return } diff --git a/cmd/cql-mysql-adapter/resolver/resolver.go b/cmd/cql-mysql-adapter/resolver/resolver.go index 362864cf6..40901efbf 100644 --- a/cmd/cql-mysql-adapter/resolver/resolver.go +++ b/cmd/cql-mysql-adapter/resolver/resolver.go @@ -291,7 +291,7 @@ func (r *Resolver) BuildParamCount(stmt sqlparser.Statement) (params int, err er params = 0 argDedup := make(map[string]bool) err = sqlparser.Walk(func(node sqlparser.SQLNode) (kontinue bool, err error) { - if v, ok := node.(*sqlparser.SQLVal); ok && v.Type == sqlparser.ValArg { + if v, ok := node.(*sqlparser.SQLVal); ok && (v.Type == sqlparser.ValArg || v.Type == sqlparser.PosArg) { argVal := string(v.Val) if !argDedup[argVal] { argDedup[argVal] = true diff --git a/cmd/cql-secure-gateway/handler.go b/cmd/cql-secure-gateway/handler.go index f7f2af85f..201a37761 100644 --- a/cmd/cql-secure-gateway/handler.go +++ b/cmd/cql-secure-gateway/handler.go @@ -39,28 +39,28 @@ type autoDecryptWrapper struct { func (w *autoDecryptWrapper) Close() error { if w.rows != nil { - return w.Close() + return w.rows.Close() } return nil } func (w *autoDecryptWrapper) Columns() ([]string, error) { if w.rows != nil { - return w.Columns() + return w.rows.Columns() } return nil, nil } func (w *autoDecryptWrapper) Err() error { if w.rows != nil { - return w.Err() + return w.rows.Err() } return nil } func (w *autoDecryptWrapper) Next() bool { if w.rows != nil { - return w.Next() + return w.rows.Next() } return false @@ -441,115 +441,118 @@ func (h *Handler) ensurePrivilege(user string, rawQuery cursor.Query) (err error } // check encryption - if query.IsRead() { - // check fields to decrypt, analyzed result columns - for i, c := range query.PhysicalColumnTransformations { - // get physical columns to decrypt - var ( - pc *Column - encConfig *encryptionConfig - ) - if encConfig, pc, err = h.resolveResultColumnDecryptKey(c, query, encryptedFields); err != nil { - tb := sqlparser.NewTrackedBuffer(nil) - err = errors.Wrapf(err, "could not resolve column to decrypt: %s", tb.WriteNode(&c.ColName).String()) - return - } + /* + if query.IsRead() { + // check fields to decrypt, analyzed result columns + for i, c := range query.PhysicalColumnTransformations { + // get physical columns to decrypt + var ( + pc *Column + encConfig *encryptionConfig + ) + if encConfig, pc, err = h.resolveResultColumnDecryptKey(c, query, encryptedFields); err != nil { + tb := sqlparser.NewTrackedBuffer(nil) + err = errors.Wrapf(err, "could not resolve column to decrypt: %s", tb.WriteNode(&c.ColName).String()) + return + } - if pc != nil { - query.DecryptConfig[i] = encConfig - } - } - } else { - // check field binding parameters - err = h.walkColumns(func(col *Column) (kontinue bool, err error) { - if col == nil || col.Computation == nil { - return false, nil - } else if col.Computation.Op != AssignmentOp { - return true, nil + if pc != nil { + query.DecryptConfig[i] = encConfig + } } + } else { + // check field binding parameters + err = h.walkColumns(func(col *Column) (kontinue bool, err error) { + if col == nil || col.Computation == nil { + return false, nil + } else if col.Computation.Op != AssignmentOp { + return true, nil + } - // process assignment - if len(col.Computation.Operands) != 2 { - tb := sqlparser.NewTrackedBuffer(nil) - err = errors.Wrapf(ErrQueryLogicError, - "should provide bind parameters for encrypted field: %s", tb.WriteNode(&col.ColName).String()) - return - } + // process assignment + if len(col.Computation.Operands) != 2 { + tb := sqlparser.NewTrackedBuffer(nil) + err = errors.Wrapf(ErrQueryLogicError, + "should provide bind parameters for encrypted field: %s", tb.WriteNode(&col.ColName).String()) + return + } - // first operand should be a physical column - if !col.Computation.Operands[0].IsPhysical { - tb := sqlparser.NewTrackedBuffer(nil) - err = errors.Wrapf(ErrQueryLogicError, - "query not supported for encryption", tb.WriteNode(&col.ColName).String()) - return - } + // first operand should be a physical column + if !col.Computation.Operands[0].IsPhysical { + tb := sqlparser.NewTrackedBuffer(nil) + err = errors.Wrapf(ErrQueryLogicError, + "query not supported for encryption, field is not a real database column: %s", tb.WriteNode(&col.ColName).String()) + return + } - // second operand should be a bind parameter value - if col.Computation.Operands[1].Computation == nil || col.Computation.Operands[1].Computation.Op != ParameterOp { - tb := sqlparser.NewTrackedBuffer(nil) - err = errors.Wrapf(ErrQueryLogicError, - "query not supported for encryption", tb.WriteNode(&col.ColName).String()) - return - } + // second operand should be a bind parameter value + if col.Computation.Operands[1].Computation == nil || col.Computation.Operands[1].Computation.Op != ParameterOp { + tb := sqlparser.NewTrackedBuffer(nil) + err = errors.Wrapf(ErrQueryLogicError, + "query not supported for encryption, value not parameter: %s", tb.WriteNode(&col.ColName).String()) + return + } - var ( - paramVal *sqlparser.SQLVal - ok bool - ) + var ( + paramVal *sqlparser.SQLVal + ok bool + ) - if paramVal, ok = col.Computation.Operands[1].Computation.Data.(*sqlparser.SQLVal); !ok || - paramVal.Type != sqlparser.PosArg && paramVal.Type != sqlparser.ValArg { - tb := sqlparser.NewTrackedBuffer(nil) - err = errors.Wrapf(ErrQueryLogicError, - "column assigned value not supported for encryption", tb.WriteNode(&col.ColName).String()) - return - } - if paramVal.Type == sqlparser.ValArg { - tb := sqlparser.NewTrackedBuffer(nil) - err = errors.Wrapf(ErrQueryLogicError, - "column assigned name argument temporary not supported for encryption", tb.WriteNode(&col.ColName).String()) - return - } + if paramVal, ok = col.Computation.Operands[1].Computation.Data.(*sqlparser.SQLVal); !ok || + paramVal.Type != sqlparser.PosArg && paramVal.Type != sqlparser.ValArg { + tb := sqlparser.NewTrackedBuffer(nil) + err = errors.Wrapf(ErrQueryLogicError, + "column assigned value not supported for encryption: %s", tb.WriteNode(&col.ColName).String()) + return + } + if paramVal.Type == sqlparser.ValArg { + tb := sqlparser.NewTrackedBuffer(nil) + err = errors.Wrapf(ErrQueryLogicError, + "column assigned named argument temporary not supported for encryption: %s", tb.WriteNode(&col.ColName).String()) + return + } - // find column encryption config - var ( - f *cw.Field - paramKey = string(paramVal.Val) - paramPos int - ) + // find column encryption config + var ( + f *cw.Field + paramKey = string(paramVal.Val) + paramPos int + ) - // convert parameter key to parameter position - if len(paramKey) < 2 { - tb := sqlparser.NewTrackedBuffer(nil) - err = errors.Wrapf(ErrQueryLogicError, - "invalid column value assignment", tb.WriteNode(&col.ColName).String()) - return - } - if paramPos, err = strconv.Atoi(paramKey[2:]); err != nil { - tb := sqlparser.NewTrackedBuffer(nil) - err = errors.Wrapf(err, "resolve parameter offset for column %s failed", tb.WriteNode(&col.ColName).String()) - return - } - if f, err = cw.NewField(query.GetDatabase(), col.ColName.Qualifier.Name.String(), col.ColName.Name.String()); err != nil { - tb := sqlparser.NewTrackedBuffer(nil) - err = errors.Wrapf(err, "could not resolve column to encrypt: %s", tb.WriteNode(&col.ColName).String()) - return - } + // convert parameter key to parameter position + if len(paramKey) < 2 { + tb := sqlparser.NewTrackedBuffer(nil) + err = errors.Wrapf(ErrQueryLogicError, + "invalid column value assignment: %s", tb.WriteNode(&col.ColName).String()) + return + } + if paramPos, err = strconv.Atoi(paramKey[2:]); err != nil { + tb := sqlparser.NewTrackedBuffer(nil) + err = errors.Wrapf(err, "resolve parameter offset for column %s failed", tb.WriteNode(&col.ColName).String()) + return + } + if f, err = cw.NewField(query.GetDatabase(), col.ColName.Qualifier.Name.String(), col.ColName.Name.String()); err != nil { + tb := sqlparser.NewTrackedBuffer(nil) + err = errors.Wrapf(err, "could not resolve column to encrypt: %s", tb.WriteNode(&col.ColName).String()) + return + } + + var exists bool + if query.EncryptConfig[paramPos], exists = encryptedFields[f.String()]; !exists { + tb := sqlparser.NewTrackedBuffer(nil) + err = errors.Wrapf(err, "could not resolve column encryption config: %s", tb.WriteNode(&col.ColName).String()) + return + } - var exists bool - if query.EncryptConfig[paramPos], exists = encryptedFields[f.String()]; !exists { - tb := sqlparser.NewTrackedBuffer(nil) - err = errors.Wrapf(err, "could not resolve column encryption config: %s", tb.WriteNode(&col.ColName).String()) + return false, nil + }, query.PhysicalColumnTransformations) + if err != nil { + err = errors.Wrap(err, "could not resolve column for encryption") return } - - return false, nil - }, query.PhysicalColumnTransformations) - if err != nil { - err = errors.Wrap(err, "could not resolve column for encryption") - return } - } + + */ return } diff --git a/cmd/cql-secure-gateway/main.go b/cmd/cql-secure-gateway/main.go index 490fd26c3..d9a5fb4ff 100644 --- a/cmd/cql-secure-gateway/main.go +++ b/cmd/cql-secure-gateway/main.go @@ -47,6 +47,7 @@ func init() { } func main() { + log.SetLevel(log.DebugLevel) flag.Parse() if showVersion { fmt.Printf("%v %v %v %v %v\n", diff --git a/cmd/cql-secure-gateway/resolver.go b/cmd/cql-secure-gateway/resolver.go index d5f4c3205..4216ffe5c 100644 --- a/cmd/cql-secure-gateway/resolver.go +++ b/cmd/cql-secure-gateway/resolver.go @@ -19,9 +19,10 @@ package main import ( "strings" - "github.com/CovenantSQL/CovenantSQL/cmd/cql-mysql-adapter/resolver" "github.com/CovenantSQL/sqlparser" "github.com/pkg/errors" + + "github.com/CovenantSQL/CovenantSQL/cmd/cql-mysql-adapter/resolver" ) var ( @@ -99,6 +100,14 @@ type Query struct { EncryptConfig map[int]*encryptionConfig } +func NewQuery(q *resolver.Query) *Query { + return &Query{ + Query: q, + DecryptConfig: make(map[int]*encryptionConfig), + EncryptConfig: make(map[int]*encryptionConfig), + } +} + // Columns defines the column array for resolving. type Columns []*Column @@ -276,7 +285,7 @@ func (r *Resolver) buildExpression(dbID string, expr sqlparser.Expr, originColum Data: e, }, }) - case sqlparser.ValArg: + case sqlparser.ValArg, sqlparser.PosArg: cols = NewColumns(&Column{ IsPhysical: false, Computation: &Computation{ @@ -1189,7 +1198,7 @@ func (r *Resolver) ResolveQuery(dbID string, query string) (q []*Query, err erro } for _, query := range queries { - newQuery := &Query{Query: query} + newQuery := NewQuery(query) if err = r.buildResolveResult(dbID, newQuery); err != nil { return } @@ -1201,7 +1210,7 @@ func (r *Resolver) ResolveQuery(dbID string, query string) (q []*Query, err erro // ResolveSingleQuery parse string query and make sure there is only one query in the query string. func (r *Resolver) ResolveSingleQuery(dbID string, queryStr string) (q *Query, err error) { - q = &Query{} + q = NewQuery(nil) if q.Query, err = r.Resolver.ResolveSingleQuery(dbID, queryStr); err != nil { return } @@ -1211,7 +1220,7 @@ func (r *Resolver) ResolveSingleQuery(dbID string, queryStr string) (q *Query, e // Resolve process sqlparser statement and returns resolved result. func (r *Resolver) Resolve(dbID string, stmt sqlparser.Statement) (q *Query, err error) { - q = &Query{} + q = NewQuery(nil) if q.Query, err = r.Resolver.Resolve(dbID, stmt); err != nil { return From eb17e51e5ad3b6196d6448cf591c9fa50a5ebb17 Mon Sep 17 00:00:00 2001 From: Qi Xiao Date: Thu, 6 Jun 2019 10:49:21 +0800 Subject: [PATCH 22/24] Add select last_request_hash feature --- cmd/cql-mysql-adapter/cursor/cursor.go | 81 +++++++++++++++++----- cmd/cql-mysql-adapter/cursor/handler.go | 10 +-- cmd/cql-mysql-adapter/handler/handler.go | 27 ++++++-- cmd/cql-mysql-adapter/resolver/meta.go | 6 +- cmd/cql-mysql-adapter/resolver/xxx_test.go | 8 ++- cmd/cql-secure-gateway/handler.go | 12 ++-- 6 files changed, 110 insertions(+), 34 deletions(-) diff --git a/cmd/cql-mysql-adapter/cursor/cursor.go b/cmd/cql-mysql-adapter/cursor/cursor.go index 8ac64afd8..e638f9f64 100644 --- a/cmd/cql-mysql-adapter/cursor/cursor.go +++ b/cmd/cql-mysql-adapter/cursor/cursor.go @@ -23,14 +23,17 @@ import ( "regexp" "strings" "sync" + "sync/atomic" - "github.com/CovenantSQL/CovenantSQL/utils/log" my "github.com/siddontang/go-mysql/mysql" + + "github.com/CovenantSQL/CovenantSQL/crypto/hash" + "github.com/CovenantSQL/CovenantSQL/utils/log" ) var ( dbIDRegex = regexp.MustCompile("^[a-zA-Z0-9_]+$") - specialSelectQuery = regexp.MustCompile("^(?i)SELECT\\s+(DATABASE|USER)\\(\\)\\s*;?\\s*$") + specialSelectQuery = regexp.MustCompile("^(?i)SELECT\\s+(DATABASE|USER|LAST_REQUEST_HASH)\\(\\)\\s*;?\\s*$") emptyResultQuery = regexp.MustCompile("^(?i)\\s*(?:/\\*.*?\\*/)?\\s*(?:SET|ROLLBACK).*$") emptyResultWithResultSetQuery = regexp.MustCompile("^(?i)\\s*(?:/\\*.*?\\*/)?\\s*(?:(?:SELECT\\s+)?@@(?:\\w+\\.)?|SHOW\\s+WARNINGS).*$") showVariablesQuery = regexp.MustCompile("^(?i)\\s*(?:/\\*.*?\\*/)?\\s*SHOW\\s+VARIABLES.*$") @@ -51,15 +54,21 @@ var ( // Cursor is a mysql connection handler, like a cursor of normal database. type Cursor struct { - curUser string - curDBLock sync.RWMutex - curDB string - h Handler + lastRequestHash atomic.Value + curUser string + curDBLock sync.RWMutex + curDB string + h Handler } // NewCursor returns a new cursor. func NewCursor(h Handler) (c *Cursor) { - return &Cursor{h: h} + c = &Cursor{ + h: h, + } + c.lastRequestHash.Store(hash.Hash{}) + + return } func (c *Cursor) buildResultSet(rows Rows) (r *my.Result, err error) { @@ -235,6 +244,10 @@ func (c *Cursor) handleSpecialQuery(query string) (r *my.Result, processed bool, []string{"USER()"}, [][]interface{}{{c.curUser}}, ) + case "LAST_REQUEST_HASH": + resultSet, _ = my.BuildSimpleTextResultset( + []string{"LAST_REQUEST_HASH()"}, + [][]interface{}{{c.lastRequestHash.Load().(hash.Hash).String()}}) } r = &my.Result{ @@ -280,6 +293,12 @@ func (c *Cursor) HandleQuery(query string) (r *my.Result, err error) { log.WithField("query", query).Info("received query") + defer func() { + if err != nil { + c.lastRequestHash.Store(hash.Hash{}) + } + }() + if r, processed, err = c.handleSpecialQuery(query); processed { return } @@ -293,22 +312,34 @@ func (c *Cursor) HandleQuery(query string) (r *my.Result, err error) { // normal query if q.IsRead() { - var rows Rows - if rows, err = c.h.Query(q); err != nil { + var ( + rows Rows + rh hash.Hash + ) + if rh, rows, err = c.h.Query(q); err != nil { err = my.NewError(my.ER_UNKNOWN_ERROR, err.Error()) return } + c.lastRequestHash.Store(rh) + // build result set return c.buildResultSet(rows) } - var result sql.Result - if result, err = c.h.Exec(q); err != nil { + var ( + result sql.Result + rh hash.Hash + ) + if rh, result, err = c.h.Exec(q); err != nil { err = my.NewError(my.ER_UNKNOWN_ERROR, err.Error()) return } + log.Infof("last request hash is: %v", rh.String()) + + c.lastRequestHash.Store(rh) + lastInsertID, _ := result.LastInsertId() affectedRows, _ := result.RowsAffected() @@ -326,7 +357,7 @@ func (c *Cursor) HandleQuery(query string) (r *my.Result, err error) { func (c *Cursor) HandleFieldList(table string, fieldWildcard string) (fields []*my.Field, err error) { // send show tables command var columns Rows - if columns, err = c.h.QueryString(c.getCurDB(), fmt.Sprintf("DESC `%s`", table)); err != nil { + if _, columns, err = c.h.QueryString(c.getCurDB(), fmt.Sprintf("DESC `%s`", table)); err != nil { // wrap error err = my.NewError(my.ER_UNKNOWN_ERROR, err.Error()) return @@ -424,6 +455,12 @@ func (c *Cursor) HandleStmtPrepare(query string) (params int, columns int, conte func (c *Cursor) HandleStmtExecute(context interface{}, query string, args []interface{}) (r *my.Result, err error) { var q Query + defer func() { + if err != nil { + c.lastRequestHash.Store(hash.Hash{}) + } + }() + switch v := context.(type) { case *my.Result: // special query @@ -438,22 +475,34 @@ func (c *Cursor) HandleStmtExecute(context interface{}, query string, args []int // normal query if q.IsRead() { - var rows Rows - if rows, err = c.h.Query(q, args...); err != nil { + var ( + rows Rows + rh hash.Hash + ) + if rh, rows, err = c.h.Query(q, args...); err != nil { err = my.NewError(my.ER_UNKNOWN_ERROR, err.Error()) return } + c.lastRequestHash.Store(rh) + // build result set return c.buildResultSet(rows) } - var result sql.Result - if result, err = c.h.Exec(q, args...); err != nil { + var ( + result sql.Result + rh hash.Hash + ) + if rh, result, err = c.h.Exec(q, args...); err != nil { err = my.NewError(my.ER_UNKNOWN_ERROR, err.Error()) return } + log.Infof("last request hash is: %v", rh.String()) + + c.lastRequestHash.Store(rh) + lastInsertID, _ := result.LastInsertId() affectedRows, _ := result.RowsAffected() diff --git a/cmd/cql-mysql-adapter/cursor/handler.go b/cmd/cql-mysql-adapter/cursor/handler.go index 57e68ee7a..06e3fc0f1 100644 --- a/cmd/cql-mysql-adapter/cursor/handler.go +++ b/cmd/cql-mysql-adapter/cursor/handler.go @@ -20,6 +20,8 @@ import ( "database/sql" "github.com/CovenantSQL/sqlparser" + + "github.com/CovenantSQL/CovenantSQL/crypto/hash" ) // Query defines resolved query utilized by cursor object. @@ -48,8 +50,8 @@ type Rows interface { type Handler interface { EnsureDatabase(dbID string) error Resolve(user string, dbID string, query string) (Query, error) - Query(q Query, args ...interface{}) (Rows, error) - Exec(q Query, args ...interface{}) (sql.Result, error) - QueryString(dbID string, query string, args ...interface{}) (Rows, error) - ExecString(dbID string, query string, args ...interface{}) (sql.Result, error) + Query(q Query, args ...interface{}) (hash.Hash, Rows, error) + Exec(q Query, args ...interface{}) (hash.Hash, sql.Result, error) + QueryString(dbID string, query string, args ...interface{}) (hash.Hash, Rows, error) + ExecString(dbID string, query string, args ...interface{}) (hash.Hash, sql.Result, error) } diff --git a/cmd/cql-mysql-adapter/handler/handler.go b/cmd/cql-mysql-adapter/handler/handler.go index e1856e792..1526baf41 100644 --- a/cmd/cql-mysql-adapter/handler/handler.go +++ b/cmd/cql-mysql-adapter/handler/handler.go @@ -17,13 +17,16 @@ package handler import ( + "context" "database/sql" "sync" + "github.com/pkg/errors" + "github.com/CovenantSQL/CovenantSQL/client" "github.com/CovenantSQL/CovenantSQL/cmd/cql-mysql-adapter/cursor" "github.com/CovenantSQL/CovenantSQL/cmd/cql-mysql-adapter/resolver" - "github.com/pingcap/errors" + "github.com/CovenantSQL/CovenantSQL/crypto/hash" ) // Handler defines the mysql adapter query handler. @@ -64,7 +67,7 @@ func (h *Handler) Resolve(user string, dbID string, query string) (q cursor.Quer } // Query executes a resolved read query. -func (h *Handler) Query(q cursor.Query, args ...interface{}) (rows cursor.Rows, err error) { +func (h *Handler) Query(q cursor.Query, args ...interface{}) (rh hash.Hash, rows cursor.Rows, err error) { if !q.IsRead() { err = errors.Wrapf(resolver.ErrQueryLogicError, "not a read query") return @@ -73,7 +76,7 @@ func (h *Handler) Query(q cursor.Query, args ...interface{}) (rows cursor.Rows, } // Exec executes a resolved write query. -func (h *Handler) Exec(q cursor.Query, args ...interface{}) (res sql.Result, err error) { +func (h *Handler) Exec(q cursor.Query, args ...interface{}) (rh hash.Hash, res sql.Result, err error) { if q.IsRead() { err = errors.Wrapf(resolver.ErrQueryLogicError, "not a write query") return @@ -85,21 +88,31 @@ func (h *Handler) Exec(q cursor.Query, args ...interface{}) (res sql.Result, err } // QueryString executes a string query without resolving. -func (h *Handler) QueryString(dbID string, query string, args ...interface{}) (rows cursor.Rows, err error) { +func (h *Handler) QueryString(dbID string, query string, args ...interface{}) (rh hash.Hash, rows cursor.Rows, err error) { var db resolver.DBHandler if db, err = h.ensureDatabase(dbID); err != nil { return } - return db.Query(query, args...) + ctx := client.WithReceipt(context.Background()) + rows, err = db.QueryContext(ctx, query, args...) + if r, ok := client.GetReceipt(ctx); ok { + rh = r.RequestHash + } + return } // ExecString executes a string query without resolving. -func (h *Handler) ExecString(dbID string, query string, args ...interface{}) (result sql.Result, err error) { +func (h *Handler) ExecString(dbID string, query string, args ...interface{}) (rh hash.Hash, result sql.Result, err error) { var db resolver.DBHandler if db, err = h.ensureDatabase(dbID); err != nil { return } - return db.Exec(query, args...) + ctx := client.WithReceipt(context.Background()) + result, err = db.ExecContext(ctx, query, args...) + if r, ok := client.GetReceipt(ctx); ok { + rh = r.RequestHash + } + return } func (h *Handler) ensureDatabase(dbID string) (db resolver.DBHandler, err error) { diff --git a/cmd/cql-mysql-adapter/resolver/meta.go b/cmd/cql-mysql-adapter/resolver/meta.go index d3a0d3ef8..15a7a16fb 100644 --- a/cmd/cql-mysql-adapter/resolver/meta.go +++ b/cmd/cql-mysql-adapter/resolver/meta.go @@ -17,14 +17,16 @@ package resolver import ( + "context" "database/sql" "fmt" "strings" "sync" "time" - "github.com/CovenantSQL/CovenantSQL/utils/log" "github.com/pkg/errors" + + "github.com/CovenantSQL/CovenantSQL/utils/log" ) const ( @@ -43,6 +45,8 @@ var ( type DBHandler interface { Query(query string, args ...interface{}) (rows *sql.Rows, err error) Exec(query string, args ...interface{}) (result sql.Result, err error) + QueryContext(ctx context.Context, query string, args ...interface{}) (rows *sql.Rows, err error) + ExecContext(ctx context.Context, query string, args ...interface{}) (result sql.Result, err error) Close() error } diff --git a/cmd/cql-mysql-adapter/resolver/xxx_test.go b/cmd/cql-mysql-adapter/resolver/xxx_test.go index 47f28468f..82a19a301 100644 --- a/cmd/cql-mysql-adapter/resolver/xxx_test.go +++ b/cmd/cql-mysql-adapter/resolver/xxx_test.go @@ -17,13 +17,15 @@ package resolver import ( + "context" "crypto/rand" "database/sql" - "github.com/CovenantSQL/CovenantSQL/crypto/hash" _ "github.com/CovenantSQL/go-sqlite3-encrypt" "github.com/CovenantSQL/sqlparser" "github.com/pkg/errors" + + "github.com/CovenantSQL/CovenantSQL/crypto/hash" ) type wrapFakeDB struct { @@ -31,6 +33,10 @@ type wrapFakeDB struct { } func (d *wrapFakeDB) Query(query string, args ...interface{}) (rows *sql.Rows, err error) { + return d.QueryContext(context.Background(), query, args) +} + +func (d *wrapFakeDB) QueryContext(ctx context.Context, query string, args ...interface{}) (rows *sql.Rows, err error) { var ( stmt sqlparser.Statement showStmt *sqlparser.Show diff --git a/cmd/cql-secure-gateway/handler.go b/cmd/cql-secure-gateway/handler.go index 201a37761..220de94f6 100644 --- a/cmd/cql-secure-gateway/handler.go +++ b/cmd/cql-secure-gateway/handler.go @@ -24,12 +24,14 @@ import ( "strconv" "strings" + "github.com/CovenantSQL/sqlparser" + "github.com/pkg/errors" + "github.com/CovenantSQL/CovenantSQL/cmd/cql-mysql-adapter/cursor" "github.com/CovenantSQL/CovenantSQL/cmd/cql-mysql-adapter/handler" cw "github.com/CovenantSQL/CovenantSQL/cmd/cql-secure-gateway/casbin" "github.com/CovenantSQL/CovenantSQL/crypto" - "github.com/CovenantSQL/sqlparser" - "github.com/pkg/errors" + "github.com/CovenantSQL/CovenantSQL/crypto/hash" ) type autoDecryptWrapper struct { @@ -256,14 +258,14 @@ func (h *Handler) Resolve(user string, dbID string, query string) (q cursor.Quer } // Query executes the resolved read query with auto fields decryption. -func (h *Handler) Query(q cursor.Query, args ...interface{}) (rows cursor.Rows, err error) { +func (h *Handler) Query(q cursor.Query, args ...interface{}) (rh hash.Hash, rows cursor.Rows, err error) { if !q.IsRead() { err = errors.Wrap(ErrQueryLogicError, "not a read query") return } // execute with auto fields decryption on result rows var origRows cursor.Rows - if origRows, err = h.QueryString(q.GetDatabase(), q.GetQuery(), args...); err != nil { + if rh, origRows, err = h.QueryString(q.GetDatabase(), q.GetQuery(), args...); err != nil { return } @@ -277,7 +279,7 @@ func (h *Handler) Query(q cursor.Query, args ...interface{}) (rows cursor.Rows, } // Exec executes the resolved write query with auto fields encryption. -func (h *Handler) Exec(q cursor.Query, args ...interface{}) (res sql.Result, err error) { +func (h *Handler) Exec(q cursor.Query, args ...interface{}) (rh hash.Hash, res sql.Result, err error) { if q.IsRead() { err = errors.Wrap(ErrQueryLogicError, "not a write query") return From bbbe666c20b7f00a62b548bcb7cf1c0214227fe3 Mon Sep 17 00:00:00 2001 From: Qi Xiao Date: Thu, 6 Jun 2019 10:49:45 +0800 Subject: [PATCH 23/24] Remove pingcap errors --- Gopkg.lock | 9 - vendor/github.com/pingcap/errors/LICENSE | 23 -- vendor/github.com/pingcap/errors/README.md | 52 --- vendor/github.com/pingcap/errors/appveyor.yml | 32 -- vendor/github.com/pingcap/errors/errors.go | 324 ------------------ vendor/github.com/pingcap/errors/group.go | 42 --- .../github.com/pingcap/errors/juju_adaptor.go | 145 -------- vendor/github.com/pingcap/errors/stack.go | 226 ------------ 8 files changed, 853 deletions(-) delete mode 100644 vendor/github.com/pingcap/errors/LICENSE delete mode 100644 vendor/github.com/pingcap/errors/README.md delete mode 100644 vendor/github.com/pingcap/errors/appveyor.yml delete mode 100644 vendor/github.com/pingcap/errors/errors.go delete mode 100644 vendor/github.com/pingcap/errors/group.go delete mode 100644 vendor/github.com/pingcap/errors/juju_adaptor.go delete mode 100644 vendor/github.com/pingcap/errors/stack.go diff --git a/Gopkg.lock b/Gopkg.lock index 1f1ad23ce..9a4c7d891 100644 --- a/Gopkg.lock +++ b/Gopkg.lock @@ -373,14 +373,6 @@ revision = "bb6d471dc95d4fe11e432687f8b70ff496cf3136" version = "v1.0.0" -[[projects]] - digest = "1:38fcc1686d3aa3d0d2d911e0a6c6301d98557b863bc18fc9cf6177899f2698b7" - name = "github.com/pingcap/errors" - packages = ["."] - pruneopts = "UT" - revision = "fc6e4ce558343e6eab2450e7653502fee61d9ad6" - version = "v0.11.4" - [[projects]] digest = "1:cf31692c14422fa27c83a05292eb5cbe0fb2775972e8f1f8446a71549bd8980b" name = "github.com/pkg/errors" @@ -706,7 +698,6 @@ "github.com/lufia/iostat", "github.com/minio/blake2b-simd", "github.com/mohae/deepcopy", - "github.com/pingcap/errors", "github.com/pkg/errors", "github.com/prometheus/client_golang/prometheus", "github.com/prometheus/client_model/go", diff --git a/vendor/github.com/pingcap/errors/LICENSE b/vendor/github.com/pingcap/errors/LICENSE deleted file mode 100644 index 835ba3e75..000000000 --- a/vendor/github.com/pingcap/errors/LICENSE +++ /dev/null @@ -1,23 +0,0 @@ -Copyright (c) 2015, Dave Cheney -All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are met: - -* Redistributions of source code must retain the above copyright notice, this - list of conditions and the following disclaimer. - -* 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 HOLDER 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/pingcap/errors/README.md b/vendor/github.com/pingcap/errors/README.md deleted file mode 100644 index 6483ba2af..000000000 --- a/vendor/github.com/pingcap/errors/README.md +++ /dev/null @@ -1,52 +0,0 @@ -# errors [![Travis-CI](https://travis-ci.org/pkg/errors.svg)](https://travis-ci.org/pkg/errors) [![AppVeyor](https://ci.appveyor.com/api/projects/status/b98mptawhudj53ep/branch/master?svg=true)](https://ci.appveyor.com/project/davecheney/errors/branch/master) [![GoDoc](https://godoc.org/github.com/pkg/errors?status.svg)](http://godoc.org/github.com/pkg/errors) [![Report card](https://goreportcard.com/badge/github.com/pkg/errors)](https://goreportcard.com/report/github.com/pkg/errors) [![Sourcegraph](https://sourcegraph.com/github.com/pkg/errors/-/badge.svg)](https://sourcegraph.com/github.com/pkg/errors?badge) - -Package errors provides simple error handling primitives. - -`go get github.com/pkg/errors` - -The traditional error handling idiom in Go is roughly akin to -```go -if err != nil { - return err -} -``` -which applied recursively up the call stack results in error reports without context or debugging information. The errors package allows programmers to add context to the failure path in their code in a way that does not destroy the original value of the error. - -## Adding context to an error - -The errors.Wrap function returns a new error that adds context to the original error. For example -```go -_, err := ioutil.ReadAll(r) -if err != nil { - return errors.Wrap(err, "read failed") -} -``` -## Retrieving the cause of an error - -Using `errors.Wrap` constructs a stack of errors, adding context to the preceding error. Depending on the nature of the error it may be necessary to reverse the operation of errors.Wrap to retrieve the original error for inspection. Any error value which implements this interface can be inspected by `errors.Cause`. -```go -type causer interface { - Cause() error -} -``` -`errors.Cause` will recursively retrieve the topmost error which does not implement `causer`, which is assumed to be the original cause. For example: -```go -switch err := errors.Cause(err).(type) { -case *MyError: - // handle specifically -default: - // unknown error -} -``` - -[Read the package documentation for more information](https://godoc.org/github.com/pkg/errors). - -## Contributing - -We welcome pull requests, bug fixes and issue reports. With that said, the bar for adding new symbols to this package is intentionally set high. - -Before proposing a change, please discuss your change by raising an issue. - -## License - -BSD-2-Clause diff --git a/vendor/github.com/pingcap/errors/appveyor.yml b/vendor/github.com/pingcap/errors/appveyor.yml deleted file mode 100644 index a932eade0..000000000 --- a/vendor/github.com/pingcap/errors/appveyor.yml +++ /dev/null @@ -1,32 +0,0 @@ -version: build-{build}.{branch} - -clone_folder: C:\gopath\src\github.com\pkg\errors -shallow_clone: true # for startup speed - -environment: - GOPATH: C:\gopath - -platform: - - x64 - -# http://www.appveyor.com/docs/installed-software -install: - # some helpful output for debugging builds - - go version - - go env - # pre-installed MinGW at C:\MinGW is 32bit only - # but MSYS2 at C:\msys64 has mingw64 - - set PATH=C:\msys64\mingw64\bin;%PATH% - - gcc --version - - g++ --version - -build_script: - - go install -v ./... - -test_script: - - set PATH=C:\gopath\bin;%PATH% - - go test -v ./... - -#artifacts: -# - path: '%GOPATH%\bin\*.exe' -deploy: off diff --git a/vendor/github.com/pingcap/errors/errors.go b/vendor/github.com/pingcap/errors/errors.go deleted file mode 100644 index 2e1d3f628..000000000 --- a/vendor/github.com/pingcap/errors/errors.go +++ /dev/null @@ -1,324 +0,0 @@ -// Package errors provides simple error handling primitives. -// -// The traditional error handling idiom in Go is roughly akin to -// -// if err != nil { -// return err -// } -// -// which applied recursively up the call stack results in error reports -// without context or debugging information. The errors package allows -// programmers to add context to the failure path in their code in a way -// that does not destroy the original value of the error. -// -// Adding context to an error -// -// The errors.Annotate function returns a new error that adds context to the -// original error by recording a stack trace at the point Annotate is called, -// and the supplied message. For example -// -// _, err := ioutil.ReadAll(r) -// if err != nil { -// return errors.Annotate(err, "read failed") -// } -// -// If additional control is required the errors.AddStack and errors.WithMessage -// functions destructure errors.Annotate into its component operations of annotating -// an error with a stack trace and an a message, respectively. -// -// Retrieving the cause of an error -// -// Using errors.Annotate constructs a stack of errors, adding context to the -// preceding error. Depending on the nature of the error it may be necessary -// to reverse the operation of errors.Annotate to retrieve the original error -// for inspection. Any error value which implements this interface -// -// type causer interface { -// Cause() error -// } -// -// can be inspected by errors.Cause. errors.Cause will recursively retrieve -// the topmost error which does not implement causer, which is assumed to be -// the original cause. For example: -// -// switch err := errors.Cause(err).(type) { -// case *MyError: -// // handle specifically -// default: -// // unknown error -// } -// -// causer interface is not exported by this package, but is considered a part -// of stable public API. -// errors.Unwrap is also available: this will retrieve the next error in the chain. -// -// Formatted printing of errors -// -// All error values returned from this package implement fmt.Formatter and can -// be formatted by the fmt package. The following verbs are supported -// -// %s print the error. If the error has a Cause it will be -// printed recursively -// %v see %s -// %+v extended format. Each Frame of the error's StackTrace will -// be printed in detail. -// -// Retrieving the stack trace of an error or wrapper -// -// New, Errorf, Annotate, and Annotatef record a stack trace at the point they are invoked. -// This information can be retrieved with the StackTracer interface that returns -// a StackTrace. Where errors.StackTrace is defined as -// -// type StackTrace []Frame -// -// The Frame type represents a call site in the stack trace. Frame supports -// the fmt.Formatter interface that can be used for printing information about -// the stack trace of this error. For example: -// -// if stacked := errors.GetStackTracer(err); stacked != nil { -// for _, f := range stacked.StackTrace() { -// fmt.Printf("%+s:%d", f) -// } -// } -// -// See the documentation for Frame.Format for more details. -// -// errors.Find can be used to search for an error in the error chain. -package errors - -import ( - "fmt" - "io" -) - -// New returns an error with the supplied message. -// New also records the stack trace at the point it was called. -func New(message string) error { - return &fundamental{ - msg: message, - stack: callers(), - } -} - -// Errorf formats according to a format specifier and returns the string -// as a value that satisfies error. -// Errorf also records the stack trace at the point it was called. -func Errorf(format string, args ...interface{}) error { - return &fundamental{ - msg: fmt.Sprintf(format, args...), - stack: callers(), - } -} - -// StackTraceAware is an optimization to avoid repetitive traversals of an error chain. -// HasStack checks for this marker first. -// Annotate/Wrap and Annotatef/Wrapf will produce this marker. -type StackTraceAware interface { - HasStack() bool -} - -// HasStack tells whether a StackTracer exists in the error chain -func HasStack(err error) bool { - if errWithStack, ok := err.(StackTraceAware); ok { - return errWithStack.HasStack() - } - return GetStackTracer(err) != nil -} - -// fundamental is an error that has a message and a stack, but no caller. -type fundamental struct { - msg string - *stack -} - -func (f *fundamental) Error() string { return f.msg } - -func (f *fundamental) Format(s fmt.State, verb rune) { - switch verb { - case 'v': - if s.Flag('+') { - io.WriteString(s, f.msg) - f.stack.Format(s, verb) - return - } - fallthrough - case 's': - io.WriteString(s, f.msg) - case 'q': - fmt.Fprintf(s, "%q", f.msg) - } -} - -// WithStack annotates err with a stack trace at the point WithStack was called. -// If err is nil, WithStack returns nil. -// -// For most use cases this is deprecated and AddStack should be used (which will ensure just one stack trace). -// However, one may want to use this in some situations, for example to create a 2nd trace across a goroutine. -func WithStack(err error) error { - if err == nil { - return nil - } - - return &withStack{ - err, - callers(), - } -} - -// AddStack is similar to WithStack. -// However, it will first check with HasStack to see if a stack trace already exists in the causer chain before creating another one. -func AddStack(err error) error { - if HasStack(err) { - return err - } - return WithStack(err) -} - -type withStack struct { - error - *stack -} - -func (w *withStack) Cause() error { return w.error } - -func (w *withStack) Format(s fmt.State, verb rune) { - switch verb { - case 'v': - if s.Flag('+') { - fmt.Fprintf(s, "%+v", w.Cause()) - w.stack.Format(s, verb) - return - } - fallthrough - case 's': - io.WriteString(s, w.Error()) - case 'q': - fmt.Fprintf(s, "%q", w.Error()) - } -} - -// Wrap returns an error annotating err with a stack trace -// at the point Wrap is called, and the supplied message. -// If err is nil, Wrap returns nil. -// -// For most use cases this is deprecated in favor of Annotate. -// Annotate avoids creating duplicate stack traces. -func Wrap(err error, message string) error { - if err == nil { - return nil - } - hasStack := HasStack(err) - err = &withMessage{ - cause: err, - msg: message, - causeHasStack: hasStack, - } - return &withStack{ - err, - callers(), - } -} - -// Wrapf returns an error annotating err with a stack trace -// at the point Wrapf is call, and the format specifier. -// If err is nil, Wrapf returns nil. -// -// For most use cases this is deprecated in favor of Annotatef. -// Annotatef avoids creating duplicate stack traces. -func Wrapf(err error, format string, args ...interface{}) error { - if err == nil { - return nil - } - hasStack := HasStack(err) - err = &withMessage{ - cause: err, - msg: fmt.Sprintf(format, args...), - causeHasStack: hasStack, - } - return &withStack{ - err, - callers(), - } -} - -// WithMessage annotates err with a new message. -// If err is nil, WithMessage returns nil. -func WithMessage(err error, message string) error { - if err == nil { - return nil - } - return &withMessage{ - cause: err, - msg: message, - causeHasStack: HasStack(err), - } -} - -type withMessage struct { - cause error - msg string - causeHasStack bool -} - -func (w *withMessage) Error() string { return w.msg + ": " + w.cause.Error() } -func (w *withMessage) Cause() error { return w.cause } -func (w *withMessage) HasStack() bool { return w.causeHasStack } - -func (w *withMessage) Format(s fmt.State, verb rune) { - switch verb { - case 'v': - if s.Flag('+') { - fmt.Fprintf(s, "%+v\n", w.Cause()) - io.WriteString(s, w.msg) - return - } - fallthrough - case 's', 'q': - io.WriteString(s, w.Error()) - } -} - -// Cause returns the underlying cause of the error, if possible. -// An error value has a cause if it implements the following -// interface: -// -// type causer interface { -// Cause() error -// } -// -// If the error does not implement Cause, the original error will -// be returned. If the error is nil, nil will be returned without further -// investigation. -func Cause(err error) error { - cause := Unwrap(err) - if cause == nil { - return err - } - return Cause(cause) -} - -// Unwrap uses causer to return the next error in the chain or nil. -// This goes one-level deeper, whereas Cause goes as far as possible -func Unwrap(err error) error { - type causer interface { - Cause() error - } - if unErr, ok := err.(causer); ok { - return unErr.Cause() - } - return nil -} - -// Find an error in the chain that matches a test function. -// returns nil if no error is found. -func Find(origErr error, test func(error) bool) error { - var foundErr error - WalkDeep(origErr, func(err error) bool { - if test(err) { - foundErr = err - return true - } - return false - }) - return foundErr -} diff --git a/vendor/github.com/pingcap/errors/group.go b/vendor/github.com/pingcap/errors/group.go deleted file mode 100644 index e5a969ab7..000000000 --- a/vendor/github.com/pingcap/errors/group.go +++ /dev/null @@ -1,42 +0,0 @@ -package errors - -// ErrorGroup is an interface for multiple errors that are not a chain. -// This happens for example when executing multiple operations in parallel. -type ErrorGroup interface { - Errors() []error -} - -// Errors uses the ErrorGroup interface to return a slice of errors. -// If the ErrorGroup interface is not implemented it returns an array containing just the given error. -func Errors(err error) []error { - if eg, ok := err.(ErrorGroup); ok { - return eg.Errors() - } - return []error{err} -} - -// WalkDeep does a depth-first traversal of all errors. -// Any ErrorGroup is traversed (after going deep). -// The visitor function can return true to end the traversal early -// In that case, WalkDeep will return true, otherwise false. -func WalkDeep(err error, visitor func(err error) bool) bool { - // Go deep - unErr := err - for unErr != nil { - if done := visitor(unErr); done { - return true - } - unErr = Unwrap(unErr) - } - - // Go wide - if group, ok := err.(ErrorGroup); ok { - for _, err := range group.Errors() { - if early := WalkDeep(err, visitor); early { - return true - } - } - } - - return false -} diff --git a/vendor/github.com/pingcap/errors/juju_adaptor.go b/vendor/github.com/pingcap/errors/juju_adaptor.go deleted file mode 100644 index ece86cd6c..000000000 --- a/vendor/github.com/pingcap/errors/juju_adaptor.go +++ /dev/null @@ -1,145 +0,0 @@ -package errors - -import ( - "fmt" - "strings" -) - -// ==================== juju adaptor start ======================== - -// Trace just calls AddStack. -func Trace(err error) error { - if err == nil { - return nil - } - return AddStack(err) -} - -// Annotate adds a message and ensures there is a stack trace. -func Annotate(err error, message string) error { - if err == nil { - return nil - } - hasStack := HasStack(err) - err = &withMessage{ - cause: err, - msg: message, - causeHasStack: hasStack, - } - if hasStack { - return err - } - return &withStack{ - err, - callers(), - } -} - -// Annotatef adds a message and ensures there is a stack trace. -func Annotatef(err error, format string, args ...interface{}) error { - if err == nil { - return nil - } - hasStack := HasStack(err) - err = &withMessage{ - cause: err, - msg: fmt.Sprintf(format, args...), - causeHasStack: hasStack, - } - if hasStack { - return err - } - return &withStack{ - err, - callers(), - } -} - -var emptyStack stack - -// NewNoStackError creates error without error stack -// later duplicate trace will no longer generate Stack too. -func NewNoStackError(msg string) error { - return &fundamental{ - msg: msg, - stack: &emptyStack, - } -} - -// SuspendStack suspends stack generate for error. -func SuspendStack(err error) error { - if err == nil { - return err - } - cleared := clearStack(err) - if cleared { - return err - } - return &withStack{ - err, - &emptyStack, - } -} - -func clearStack(err error) (cleared bool) { - switch typedErr := err.(type) { - case *withMessage: - return clearStack(typedErr.Cause()) - case *fundamental: - typedErr.stack = &emptyStack - return true - case *withStack: - typedErr.stack = &emptyStack - clearStack(typedErr.Cause()) - return true - default: - return false - } -} - -// ErrorStack will format a stack trace if it is available, otherwise it will be Error() -// If the error is nil, the empty string is returned -// Note that this just calls fmt.Sprintf("%+v", err) -func ErrorStack(err error) string { - if err == nil { - return "" - } - return fmt.Sprintf("%+v", err) -} - -// IsNotFound reports whether err was not found error. -func IsNotFound(err error) bool { - return strings.Contains(err.Error(), "not found") -} - -// NotFoundf represents an error with not found message. -func NotFoundf(format string, args ...interface{}) error { - return Errorf(format+" not found", args...) -} - -// BadRequestf represents an error with bad request message. -func BadRequestf(format string, args ...interface{}) error { - return Errorf(format+" bad request", args...) -} - -// NotSupportedf represents an error with not supported message. -func NotSupportedf(format string, args ...interface{}) error { - return Errorf(format+" not supported", args...) -} - -// NotValidf represents an error with not valid message. -func NotValidf(format string, args ...interface{}) error { - return Errorf(format+" not valid", args...) -} - -// IsAlreadyExists reports whether err was already exists error. -func IsAlreadyExists(err error) bool { - return strings.Contains(err.Error(), "already exists") -} - -// AlreadyExistsf represents an error with already exists message. -func AlreadyExistsf(format string, args ...interface{}) error { - return Errorf(format+" already exists", args...) -} - -// ==================== juju adaptor end ======================== diff --git a/vendor/github.com/pingcap/errors/stack.go b/vendor/github.com/pingcap/errors/stack.go deleted file mode 100644 index bb1e6a84f..000000000 --- a/vendor/github.com/pingcap/errors/stack.go +++ /dev/null @@ -1,226 +0,0 @@ -package errors - -import ( - "bytes" - "fmt" - "io" - "path" - "runtime" - "strconv" - "strings" -) - -// StackTracer retrieves the StackTrace -// Generally you would want to use the GetStackTracer function to do that. -type StackTracer interface { - StackTrace() StackTrace -} - -// GetStackTracer will return the first StackTracer in the causer chain. -// This function is used by AddStack to avoid creating redundant stack traces. -// -// You can also use the StackTracer interface on the returned error to get the stack trace. -func GetStackTracer(origErr error) StackTracer { - var stacked StackTracer - WalkDeep(origErr, func(err error) bool { - if stackTracer, ok := err.(StackTracer); ok { - stacked = stackTracer - return true - } - return false - }) - return stacked -} - -// Frame represents a program counter inside a stack frame. -type Frame uintptr - -// pc returns the program counter for this frame; -// multiple frames may have the same PC value. -func (f Frame) pc() uintptr { return uintptr(f) - 1 } - -// file returns the full path to the file that contains the -// function for this Frame's pc. -func (f Frame) file() string { - fn := runtime.FuncForPC(f.pc()) - if fn == nil { - return "unknown" - } - file, _ := fn.FileLine(f.pc()) - return file -} - -// line returns the line number of source code of the -// function for this Frame's pc. -func (f Frame) line() int { - fn := runtime.FuncForPC(f.pc()) - if fn == nil { - return 0 - } - _, line := fn.FileLine(f.pc()) - return line -} - -// Format formats the frame according to the fmt.Formatter interface. -// -// %s source file -// %d source line -// %n function name -// %v equivalent to %s:%d -// -// Format accepts flags that alter the printing of some verbs, as follows: -// -// %+s function name and path of source file relative to the compile time -// GOPATH separated by \n\t (\n\t) -// %+v equivalent to %+s:%d -func (f Frame) Format(s fmt.State, verb rune) { - f.format(s, s, verb) -} - -// format allows stack trace printing calls to be made with a bytes.Buffer. -func (f Frame) format(w io.Writer, s fmt.State, verb rune) { - switch verb { - case 's': - switch { - case s.Flag('+'): - pc := f.pc() - fn := runtime.FuncForPC(pc) - if fn == nil { - io.WriteString(w, "unknown") - } else { - file, _ := fn.FileLine(pc) - io.WriteString(w, fn.Name()) - io.WriteString(w, "\n\t") - io.WriteString(w, file) - } - default: - io.WriteString(w, path.Base(f.file())) - } - case 'd': - io.WriteString(w, strconv.Itoa(f.line())) - case 'n': - name := runtime.FuncForPC(f.pc()).Name() - io.WriteString(w, funcname(name)) - case 'v': - f.format(w, s, 's') - io.WriteString(w, ":") - f.format(w, s, 'd') - } -} - -// StackTrace is stack of Frames from innermost (newest) to outermost (oldest). -type StackTrace []Frame - -// Format formats the stack of Frames according to the fmt.Formatter interface. -// -// %s lists source files for each Frame in the stack -// %v lists the source file and line number for each Frame in the stack -// -// Format accepts flags that alter the printing of some verbs, as follows: -// -// %+v Prints filename, function, and line number for each Frame in the stack. -func (st StackTrace) Format(s fmt.State, verb rune) { - var b bytes.Buffer - switch verb { - case 'v': - switch { - case s.Flag('+'): - b.Grow(len(st) * stackMinLen) - for _, fr := range st { - b.WriteByte('\n') - fr.format(&b, s, verb) - } - case s.Flag('#'): - fmt.Fprintf(&b, "%#v", []Frame(st)) - default: - st.formatSlice(&b, s, verb) - } - case 's': - st.formatSlice(&b, s, verb) - } - io.Copy(s, &b) -} - -// formatSlice will format this StackTrace into the given buffer as a slice of -// Frame, only valid when called with '%s' or '%v'. -func (st StackTrace) formatSlice(b *bytes.Buffer, s fmt.State, verb rune) { - b.WriteByte('[') - if len(st) == 0 { - b.WriteByte(']') - return - } - - b.Grow(len(st) * (stackMinLen / 4)) - st[0].format(b, s, verb) - for _, fr := range st[1:] { - b.WriteByte(' ') - fr.format(b, s, verb) - } - b.WriteByte(']') -} - -// stackMinLen is a best-guess at the minimum length of a stack trace. It -// doesn't need to be exact, just give a good enough head start for the buffer -// to avoid the expensive early growth. -const stackMinLen = 96 - -// stack represents a stack of program counters. -type stack []uintptr - -func (s *stack) Format(st fmt.State, verb rune) { - switch verb { - case 'v': - switch { - case st.Flag('+'): - var b bytes.Buffer - b.Grow(len(*s) * stackMinLen) - for _, pc := range *s { - f := Frame(pc) - b.WriteByte('\n') - f.format(&b, st, 'v') - } - io.Copy(st, &b) - } - } -} - -func (s *stack) StackTrace() StackTrace { - f := make([]Frame, len(*s)) - for i := 0; i < len(f); i++ { - f[i] = Frame((*s)[i]) - } - return f -} - -func callers() *stack { - return callersSkip(4) -} - -func callersSkip(skip int) *stack { - const depth = 32 - var pcs [depth]uintptr - n := runtime.Callers(skip, pcs[:]) - var st stack = pcs[0:n] - return &st -} - -// funcname removes the path prefix component of a function's name reported by func.Name(). -func funcname(name string) string { - i := strings.LastIndex(name, "/") - name = name[i+1:] - i = strings.Index(name, ".") - return name[i+1:] -} - -// NewStack is for library implementers that want to generate a stack trace. -// Normally you should insted use AddStack to get an error with a stack trace. -// -// The result of this function can be turned into a stack trace by calling .StackTrace() -// -// This function takes an argument for the number of stack frames to skip. -// This avoids putting stack generation function calls like this one in the stack trace. -// A value of 0 will give you the line that called NewStack(0) -// A library author wrapping this in their own function will want to use a value of at least 1. -func NewStack(skip int) StackTracer { - return callersSkip(skip + 3) -} From 6c3ef2ea908dcde2b65ab641c6d9681849dd81cc Mon Sep 17 00:00:00 2001 From: auxten Date: Thu, 13 Jun 2019 17:03:55 +0800 Subject: [PATCH 24/24] Modify mysql-adapter MetaRefreshInterval from 1min to 8hours --- cmd/cql-mysql-adapter/resolver/meta.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmd/cql-mysql-adapter/resolver/meta.go b/cmd/cql-mysql-adapter/resolver/meta.go index 15a7a16fb..23f297e04 100644 --- a/cmd/cql-mysql-adapter/resolver/meta.go +++ b/cmd/cql-mysql-adapter/resolver/meta.go @@ -31,7 +31,7 @@ import ( const ( // MetaRefreshInterval defines the default database meta re-scan interval. - MetaRefreshInterval = time.Minute + MetaRefreshInterval = 8 * time.Hour ) var (