From 39c1e09d6073da07ed4ffa5554ff7cf5c59a262e Mon Sep 17 00:00:00 2001 From: Miguel Molina Date: Thu, 11 Apr 2019 10:56:52 +0200 Subject: [PATCH 1/4] tools: make upgrade tool copy docs from go-mysql-server Fixes #793 There are some docs we want to have in gitbase as well as go-mysql-server. To avoid possibly having different versions in each project, this will automatically copy certain parts and files from one to the other when there is an update. Signed-off-by: Miguel Molina --- tools/rev-upgrade/main.go | 132 ++++++++++++++++++++++++++++++++++++-- 1 file changed, 128 insertions(+), 4 deletions(-) diff --git a/tools/rev-upgrade/main.go b/tools/rev-upgrade/main.go index 15b8178fb..845a4ee48 100644 --- a/tools/rev-upgrade/main.go +++ b/tools/rev-upgrade/main.go @@ -7,16 +7,21 @@ import ( "io" "io/ioutil" "log" + "os" "os/exec" "path/filepath" "regexp" + "strings" "sync" "github.com/BurntSushi/toml" git "gopkg.in/src-d/go-git.v4" ) -const lockFile = "Gopkg.lock" +const ( + lockFile = "Gopkg.lock" + goMysqlServer = "gopkg.in/src-d/go-mysql-server.v0" +) type project struct { Name string @@ -44,7 +49,7 @@ func main() { err error ) - flag.StringVar(&prj, "p", "gopkg.in/src-d/go-mysql-server.v0", "project name (e.g.: gopkg.in/src-d/go-mysql-server.v0)") + flag.StringVar(&prj, "p", goMysqlServer, "project name (e.g.: gopkg.in/src-d/go-mysql-server.v0)") flag.StringVar(&newRev, "r", "", "revision (by default the latest allowed by Gopkg.toml)") flag.Parse() @@ -81,11 +86,16 @@ func main() { } } - err = ensure(prj) - if err != nil { + if err = ensure(prj); err != nil { return } + if prj == goMysqlServer { + if err = importDocs(); err != nil { + return + } + } + if newRev == "" { newRev, err = revision(filepath.Join(w.Filesystem.Root(), "Gopkg.lock"), prj) fmt.Printf("Project: %s\nOld rev: %s\nNew rev: %s\n", prj, oldRev, newRev) @@ -190,3 +200,117 @@ func ensure(prj string) error { return nil } + +var docsToCopy = []struct { + from []string + to []string + blocks []string +}{ + { + from: []string{"SUPPORTED.md"}, + to: []string{"docs", "using-gitbase", "supported-syntax.md"}, + }, + { + from: []string{"SUPPORTED_CLIENTS.md"}, + to: []string{"docs", "using-gitbase", "supported-clients.md"}, + }, + { + from: []string{"README.md"}, + to: []string{"docs", "using-gitbase", "functions.md"}, + blocks: []string{"FUNCTIONS"}, + }, + { + from: []string{"README.md"}, + to: []string{"docs", "using-gitbase", "configuration.md"}, + blocks: []string{"CONFIG"}, + }, +} + +func importDocs() error { + pwd, err := os.Getwd() + if err != nil { + return err + } + dirs := strings.Split(goMysqlServer, "/") + goMysqlServerPath := filepath.Join(append([]string{pwd, "vendor"}, dirs...)...) + + for _, c := range docsToCopy { + src := filepath.Join(append([]string{goMysqlServerPath}, c.from...)...) + dst := filepath.Join(append([]string{pwd}, c.to...)...) + + if len(c.blocks) == 0 { + if err := copyFile(src, dst); err != nil { + return err + } + } else { + if err := copyFileBlocks(src, dst, c.blocks); err != nil { + return err + } + } + } + + return nil +} + +func copyFile(src, dst string) error { + fout, err := os.Create(dst) + if err != nil { + return err + } + defer fout.Close() + + fin, err := os.Open(src) + if err != nil { + return err + } + defer fin.Close() + + _, err = io.Copy(fout, fin) + return err +} + +func copyFileBlocks(src, dst string, blocks []string) error { + fout, err := ioutil.ReadFile(dst) + if err != nil { + return err + } + + fin, err := ioutil.ReadFile(src) + if err != nil { + return err + } + + for _, b := range blocks { + open := []byte(fmt.Sprintf("", b)) + close := []byte(fmt.Sprintf("", b)) + + outOpenIdx := bytes.Index(fout, open) + outCloseIdx := bytes.Index(fout, close) + inOpenIdx := bytes.Index(fin, open) + inCloseIdx := bytes.Index(fin, close) + + if outOpenIdx < 0 || outCloseIdx < 0 { + return fmt.Errorf("block %q not found on %s", b, dst) + } + + if inOpenIdx < 0 || inCloseIdx < 0 { + return fmt.Errorf("block %q not found on %s", b, src) + } + + var newOut []byte + newOut = append(newOut, fout[:outOpenIdx]...) + newOut = append(newOut, []byte(strings.TrimSpace(string(fin[inOpenIdx:inCloseIdx+len(close)])))...) + newOut = append(newOut, fout[outCloseIdx+len(close):]...) + + fout = newOut + } + + f, err := os.Create(dst) + if err != nil { + return err + } + defer f.Close() + + _, err = f.Write(fout) + return err +} From 413f1664565aad3d7ed187f79b929f9f363d58fa Mon Sep 17 00:00:00 2001 From: Miguel Molina Date: Thu, 11 Apr 2019 10:58:36 +0200 Subject: [PATCH 2/4] vendor: upgrade go-mysql-server Signed-off-by: Miguel Molina --- Gopkg.lock | 6 +- Gopkg.toml | 3 +- docs/using-gitbase/configuration.md | 14 + docs/using-gitbase/functions.md | 77 ++++- docs/using-gitbase/indexes.md | 2 +- docs/using-gitbase/supported-clients.md | 275 +++++++++++++++- docs/using-gitbase/supported-syntax.md | 124 ++++++- .../src-d/go-mysql-server.v0/.gitignore | 32 ++ .../src-d/go-mysql-server.v0/.travis.yml | 106 ++++++ .../src-d/go-mysql-server.v0/ARCHITECTURE.md | 137 ++++++++ .../src-d/go-mysql-server.v0/CONTRIBUTING.md | 62 ++++ vendor/gopkg.in/src-d/go-mysql-server.v0/DCO | 36 +++ .../src-d/go-mysql-server.v0/MAINTAINERS | 1 + .../src-d/go-mysql-server.v0/Makefile | 27 ++ .../src-d/go-mysql-server.v0/README.md | 302 ++++++++++++++++++ .../src-d/go-mysql-server.v0/SUPPORTED.md | 123 +++++++ .../go-mysql-server.v0/SUPPORTED_CLIENTS.md | 274 ++++++++++++++++ .../src-d/go-mysql-server.v0/_config.yml | 1 + .../src-d/go-mysql-server.v0/engine.go | 13 +- .../gopkg.in/src-d/go-mysql-server.v0/go.mod | 41 +++ .../gopkg.in/src-d/go-mysql-server.v0/go.sum | 144 +++++++++ .../sql/expression/function/registry.go | 127 ++++---- .../sql/functionregistry.go | 134 +++++--- .../sql/index/pilosa/driver.go | 157 ++++++--- .../sql/index/pilosa/iterator.go | 4 +- .../sql/index/pilosa/lookup.go | 23 +- .../sql/index/pilosa/mapping.go | 51 ++- .../sql/parse/show_create.go | 27 +- .../go-mysql-server.v0/sql/plan/innerjoin.go | 14 +- .../sql/plan/show_create_table.go | 5 +- 30 files changed, 2133 insertions(+), 209 deletions(-) create mode 100644 vendor/gopkg.in/src-d/go-mysql-server.v0/.gitignore create mode 100644 vendor/gopkg.in/src-d/go-mysql-server.v0/.travis.yml create mode 100644 vendor/gopkg.in/src-d/go-mysql-server.v0/ARCHITECTURE.md create mode 100644 vendor/gopkg.in/src-d/go-mysql-server.v0/CONTRIBUTING.md create mode 100644 vendor/gopkg.in/src-d/go-mysql-server.v0/DCO create mode 100644 vendor/gopkg.in/src-d/go-mysql-server.v0/MAINTAINERS create mode 100644 vendor/gopkg.in/src-d/go-mysql-server.v0/Makefile create mode 100644 vendor/gopkg.in/src-d/go-mysql-server.v0/README.md create mode 100644 vendor/gopkg.in/src-d/go-mysql-server.v0/SUPPORTED.md create mode 100644 vendor/gopkg.in/src-d/go-mysql-server.v0/SUPPORTED_CLIENTS.md create mode 100644 vendor/gopkg.in/src-d/go-mysql-server.v0/_config.yml create mode 100644 vendor/gopkg.in/src-d/go-mysql-server.v0/go.mod create mode 100644 vendor/gopkg.in/src-d/go-mysql-server.v0/go.sum diff --git a/Gopkg.lock b/Gopkg.lock index 18080eadf..b35aa2a7f 100644 --- a/Gopkg.lock +++ b/Gopkg.lock @@ -672,7 +672,7 @@ source = "github.com/src-d/go-git" [[projects]] - digest = "1:fc7e1e0d958879bb0eb442e61c264ed214077ed15e00f472998955578ab2c887" + digest = "1:3b3fa49a96ab0ff715b28b6baf0ae8a224b35dc01eaa6dd3358071dab0c01930" name = "gopkg.in/src-d/go-mysql-server.v0" packages = [ ".", @@ -690,8 +690,8 @@ "sql/parse", "sql/plan", ] - pruneopts = "NUT" - revision = "3dd441325d1731821eff0495fbf63747c258b8ff" + pruneopts = "UT" + revision = "d03de5f7c0d7b9e9920c7654efc61eabe988dabe" [[projects]] digest = "1:f995136b53497081f1b3f29b99e78a597da6afb2bc6f22908382559a863df4ea" diff --git a/Gopkg.toml b/Gopkg.toml index 066767199..ce7b43c0d 100644 --- a/Gopkg.toml +++ b/Gopkg.toml @@ -1,6 +1,6 @@ [[constraint]] name = "gopkg.in/src-d/go-mysql-server.v0" - revision = "3dd441325d1731821eff0495fbf63747c258b8ff" + revision = "d03de5f7c0d7b9e9920c7654efc61eabe988dabe" [[constraint]] name = "github.com/jessevdk/go-flags" @@ -84,7 +84,6 @@ [[prune.project]] name = "gopkg.in/src-d/go-mysql-server.v0" go-tests = true - non-go = true unused-packages = true [[prune.project]] name = "gopkg.in/src-d/go-git.v4" diff --git a/docs/using-gitbase/configuration.md b/docs/using-gitbase/configuration.md index f9f584f40..74e183c4a 100644 --- a/docs/using-gitbase/configuration.md +++ b/docs/using-gitbase/configuration.md @@ -19,6 +19,20 @@ | `GITBASE_MAX_UAST_BLOB_SIZE` | Max size of blobs to send to be parsed by bblfsh. Default: 5242880 (5MB) | | `GITBASE_LOG_LEVEL` | minimum logging level to show, use `fatal` to suppress most messages. Default: `info` | +## Configuration from `go-mysql-server` + + +| Name | Type | Description | +|:-----|:-----|:------------| +|`INMEMORY_JOINS`|environment|If set it will perform all joins in memory. Default is off.| +|`inmemory_joins`|session|If set it will perform all joins in memory. Default is off. This has precedence over `INMEMORY_JOINS`.| +|`MAX_MEMORY_INNER_JOIN`|environment|The maximum number of memory, in megabytes, that can be consumed by go-mysql-server before switching to multipass mode in inner joins. Default is the 20% of all available physical memory.| +|`max_memory_joins`|session|The maximum number of memory, in megabytes, that can be consumed by go-mysql-server before switching to multipass mode in inner joins. Default is the 20% of all available physical memory. This has precedence over `MAX_MEMORY_INNER_JOIN`.| +|`DEBUG_ANALYZER`|environment|If set, the analyzer will print debug messages. Default is off.| +|`PILOSA_INDEX_THREADS`|environment|Number of threads used in index creation. Default is the number of cores available in the machine.| +|`pilosa_index_threads`|environment|Number of threads used in index creation. Default is the number of cores available in the machine. This has precedence over `PILOSA_INDEX_THREADS`.| + + ### Jaeger tracing variables *Extracted from https://github.com/jaegertracing/jaeger-client-go/blob/master/README.md* diff --git a/docs/using-gitbase/functions.md b/docs/using-gitbase/functions.md index 600547c56..ae86f143e 100644 --- a/docs/using-gitbase/functions.md +++ b/docs/using-gitbase/functions.md @@ -6,15 +6,72 @@ To make some common tasks easier for the user, there are some functions to inter | Name | Description | |:-------------|:-------------------------------------------------------------------------------------------------------------------------------| -|is_remote(reference_name)bool| check if the given reference name is from a remote one | -|is_tag(reference_name)bool| check if the given reference name is a tag | -|language(path, [blob])text| gets the language of a file given its path and the optional content of the file | -|uast(blob, [lang, [xpath]]) blob| returns a node array of UAST nodes in semantic mode | -|uast_mode(mode, blob, lang) blob| returns a node array of UAST nodes specifying its language and mode (semantic, annotated or native) | -|uast_xpath(blob, xpath) blob| performs an XPath query over the given UAST nodes | -|uast_extract(blob, key) text array| extracts information identified by the given key from the uast nodes | -|uast_children(blob) blob| returns a flattened array of the children UAST nodes from each one of the UAST nodes in the given array | +|`is_remote(reference_name)bool`| check if the given reference name is from a remote one | +|`is_tag(reference_name)bool`| check if the given reference name is a tag | +|`language(path, [blob])text`| gets the language of a file given its path and the optional content of the file | +|`uast(blob, [lang, [xpath]]) blob`| returns a node array of UAST nodes in semantic mode | +|`uast_mode(mode, blob, lang) blob`| returns a node array of UAST nodes specifying its language and mode (semantic, annotated or native) | +|`uast_xpath(blob, xpath) blob`| performs an XPath query over the given UAST nodes | +|`uast_extract(blob, key) text array`| extracts information identified by the given key from the uast nodes | +|`uast_children(blob) blob`| returns a flattened array of the children UAST nodes from each one of the UAST nodes in the given array | +## Standard functions + +These are all functions that are available because they are implemented in `go-mysql-server`, used by gitbase. + + +| Name | Description | +|:-------------|:-------------------------------------------------------------------------------------------------------------------------------| +|`ARRAY_LENGTH(json)`|If the json representation is an array, this function returns its size.| +|`AVG(expr)`|Returns the average value of expr in all rows.| +|`CEIL(number)`|Return the smallest integer value that is greater than or equal to `number`.| +|`CEILING(number)`|Return the smallest integer value that is greater than or equal to `number`.| +|`COALESCE(...)`|The function returns the first non-null value in a list.| +|`CONCAT(...)`|Concatenate any group of fields into a single string.| +|`CONCAT_WS(sep, ...)`|Concatenate any group of fields into a single string. The first argument is the separator for the rest of the arguments. The separator is added between the strings to be concatenated. The separator can be a string, as can the rest of the arguments. If the separator is NULL, the result is NULL.| +|`CONNECTION_ID()`|Return the current connection ID.| +|`COUNT(expr)`| Returns a count of the number of non-NULL values of expr in the rows retrieved by a SELECT statement.| +|`DAY(date)`|Returns the day of the given date.| +|`DAYOFWEEK(date)`|Returns the day of the week of the given date.| +|`DAYOFYEAR(date)`|Returns the day of the year of the given date.| +|`FLOOR(number)`|Return the largest integer value that is less than or equal to `number`.| +|`HOUR(date)`|Returns the hours of the given date.| +|`IFNULL(expr1, expr2)`|If expr1 is not NULL, IFNULL() returns expr1; otherwise it returns expr2.| +|`IS_BINARY(blob)`|Returns whether a BLOB is a binary file or not.| +|`JSON_EXTRACT(json_doc, path, ...)`|Extracts data from a json document using json paths.| +|`LN(X)`|Return the natural logarithm of X.| +|`LOG(X), LOG(B, X)`|If called with one parameter, this function returns the natural logarithm of X. If called with two parameters, this function returns the logarithm of X to the base B. If X is less than or equal to 0, or if B is less than or equal to 1, then NULL is returned.| +|`LOG10(X)`|Returns the base-10 logarithm of X.| +|`LOG2(X)`|Returns the base-2 logarithm of X.| +|`LOWER(str)`|Returns the string str with all characters in lower case.| +|`LPAD(str, len, padstr)`|Return the string argument, left-padded with the specified string.| +|`LTRIM(str)`|Returns the string str with leading space characters removed.| +|`MAX(expr)`|Returns the maximum value of expr in all rows.| +|`MID(str, pos, [len])`|Return a substring from the provided string starting at `pos` with a length of `len` characters. If no `len` is provided, all characters from `pos` until the end will be taken.| +|`MIN(expr)`|Returns the minimum value of expr in all rows.| +|`MINUTE(date)`|Returns the minutes of the given date.| +|`MONTH(date)`|Returns the month of the given date.| +|`NOW()`|Returns the current timestamp.| +|`NULLIF(expr1, expr2)`|Returns NULL if expr1 = expr2 is true, otherwise returns expr1.| +|`POW(X, Y)`|Returns the value of X raised to the power of Y.| +|`REPEAT(str, count)`|Returns a string consisting of the string str repeated count times.| +|`REPLACE(str,from_str,to_str)`|Returns the string str with all occurrences of the string from_str replaced by the string to_str.| +|`REVERSE(str)`|Returns the string str with the order of the characters reversed.| +|`ROUND(number, decimals)`|Round the `number` to `decimals` decimal places.| +|`RPAD(str, len, padstr)`|Returns the string str, right-padded with the string padstr to a length of len characters.| +|`RTRIM(str)`|Returns the string str with trailing space characters removed.| +|`SECOND(date)`|Returns the seconds of the given date.| +|`SOUNDEX(str)`|Returns the soundex of a string.| +|`SPLIT(str,sep)`|Receives a string and a separator and returns the parts of the string split by the separator as a JSON array of strings.| +|`SQRT(X)`|Returns the square root of a nonnegative number X.| +|`SUBSTR(str, pos, [len])`|Return a substring from the provided string starting at `pos` with a length of `len` characters. If no `len` is provided, all characters from `pos` until the end will be taken.| +|`SUBSTRING(str, pos, [len])`|Return a substring from the provided string starting at `pos` with a length of `len` characters. If no `len` is provided, all characters from `pos` until the end will be taken.| +|`SUM(expr)`|Returns the sum of expr in all rows.| +|`TRIM(str)`|Returns the string str with all spaces removed.| +|`UPPER(str)`|Returns the string str with all characters in upper case.| +|`WEEKDAY(date)`|Returns the weekday of the given date.| +|`YEAR(date)`|Returns the year of the given date.| + ## Note about uast, uast_mode, uast_xpath and uast_children functions @@ -92,7 +149,3 @@ Nodes that have no value for the requested property will not be present in any w Also, if you want to retrieve values from a non common property, you can pass it directly > uast_extract(nodes_column, 'some-property') - -## Standard functions - -You can check standard functions in [`go-mysql-server` documentation](https://github.com/src-d/go-mysql-server/tree/3dd441325d1731821eff0495fbf63747c258b8ff#custom-functions). diff --git a/docs/using-gitbase/indexes.md b/docs/using-gitbase/indexes.md index aa26c15df..74e676e7f 100644 --- a/docs/using-gitbase/indexes.md +++ b/docs/using-gitbase/indexes.md @@ -26,4 +26,4 @@ and for the second query also two indexes will be used and the result will be a You can find some more examples in the [examples](./examples.md#create-an-index-for-columns-on-a-table) section. -See [go-mysql-server](https://github.com/src-d/go-mysql-server/tree/3dd441325d1731821eff0495fbf63747c258b8ff#indexes) documentation for more details +See [go-mysql-server](https://github.com/src-d/go-mysql-server/tree/d03de5f7c0d7b9e9920c7654efc61eabe988dabe#indexes) documentation for more details diff --git a/docs/using-gitbase/supported-clients.md b/docs/using-gitbase/supported-clients.md index 7095b02c7..e30e928e9 100644 --- a/docs/using-gitbase/supported-clients.md +++ b/docs/using-gitbase/supported-clients.md @@ -1,3 +1,274 @@ -## Supported clients +# Supported clients -To see the supported MySQL clients and examples about how to use them, take a look [here](https://github.com/src-d/go-mysql-server/blob/3dd441325d1731821eff0495fbf63747c258b8ff/SUPPORTED_CLIENTS.md). +These are the clients we actively test against to check are compatible with go-mysql-server. Other clients may also work, but we don't check on every build if we remain compatible with them. + +- Python + - [pymysql](#pymysql) + - [mysql-connector](#python-mysql-connector) + - [sqlalchemy](#python-sqlalchemy) +- Ruby + - [ruby-mysql](#ruby-mysql) +- [PHP](#php) +- Node.js + - [mysqljs/mysql](#mysqljs) +- .NET Core + - [MysqlConnector](#mysqlconnector) +- Java/JVM + - [mariadb-java-client](#mariadb-java-client) +- Go + - [go-mysql-driver/mysql](#go-mysql-driver-mysql) +- C + - [mysql-connector-c](#mysql-connector-c) +- Grafana +- Tableau Desktop + +## Example client usage + +### pymysql + +```python +import pymysql.cursors + +connection = pymysql.connect(host='127.0.0.1', + user='root', + password='', + db='mydb', + cursorclass=pymysql.cursors.DictCursor) + +try: + with connection.cursor() as cursor: + sql = "SELECT * FROM mytable LIMIT 1" + cursor.execute(sql) + rows = cursor.fetchall() + + # use rows +finally: + connection.close() +``` + +### Python mysql-connector + +```python +import mysql.connector + +connection = mysql.connector.connect(host='127.0.0.1', + user='root', + passwd='', + port=3306, + database='mydb') + +try: + cursor = connection.cursor() + sql = "SELECT * FROM mytable LIMIT 1" + cursor.execute(sql) + rows = cursor.fetchall() + + # use rows +finally: + connection.close() +``` + +### Python sqlalchemy + +```python +import pandas as pd +import sqlalchemy + +engine = sqlalchemy.create_engine('mysql+pymysql://root:@127.0.0.1:3306/mydb') +with engine.connect() as conn: + repo_df = pd.read_sql_table("mytable", con=conn) + for table_name in repo_df.to_dict(): + print(table_name) +``` + +### ruby-mysql + +```ruby +require "mysql" + +conn = Mysql::new("127.0.0.1", "root", "", "mydb") +resp = conn.query "SELECT * FROM mytable LIMIT 1" + +# use resp + +conn.close() +``` + +### php + +```php +try { + $conn = new PDO("mysql:host=127.0.0.1:3306;dbname=mydb", "root", ""); + $conn->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); + + $stmt = $conn->query('SELECT * FROM mytable LIMIT 1'); + $result = $stmt->fetchAll(PDO::FETCH_ASSOC); + + // use result +} catch (PDOException $e) { + // handle error +} +``` + +### mysqljs + +```js +import mysql from 'mysql'; + +const connection = mysql.createConnection({ + host: '127.0.0.1', + port: 3306, + user: 'root', + password: '', + database: 'mydb' +}); +connection.connect(); + +const query = 'SELECT * FROM mytable LIMIT 1'; +connection.query(query, function (error, results, _) { + if (error) throw error; + + // use results +}); + +connection.end(); +``` + +### MysqlConnector + +```csharp +using MySql.Data.MySqlClient; +using System.Threading.Tasks; + +namespace something +{ + public class Something + { + public async Task DoQuery() + { + var connectionString = "server=127.0.0.1;user id=root;password=;port=3306;database=mydb;"; + + using (var conn = new MySqlConnection(connectionString)) + { + await conn.OpenAsync(); + + var sql = "SELECT * FROM mytable LIMIT 1"; + + using (var cmd = new MySqlCommand(sql, conn)) + using (var reader = await cmd.ExecuteReaderAsync()) + while (await reader.ReadAsync()) { + // use reader + } + } + } + } +} +``` + +### mariadb-java-client + +```java +package org.testing.mariadbjavaclient; + +import java.sql.*; + +class Main { + public static void main(String[] args) { + String dbUrl = "jdbc:mariadb://127.0.0.1:3306/mydb?user=root&password="; + String query = "SELECT * FROM mytable LIMIT 1"; + + try (Connection connection = DriverManager.getConnection(dbUrl)) { + try (PreparedStatement stmt = connection.prepareStatement(query)) { + try (ResultSet rs = stmt.executeQuery()) { + while (rs.next()) { + // use rs + } + } + } + } catch (SQLException e) { + // handle failure + } + } +} +``` + +### go-sql-driver/mysql + +```go +package main + +import ( + "database/sql" + + _ "github.com/go-sql-driver/mysql" +) + +func main() { + db, err := sql.Open("mysql", "root:@tcp(127.0.0.1:3306)/mydb") + if err != nil { + // handle error + } + + rows, err := db.Query("SELECT * FROM mytable LIMIT 1") + if err != nil { + // handle error + } + + // use rows +} +``` + +### #mysql-connector-c + +```c +#include +#include + +void finish_with_error(MYSQL *con) +{ + fprintf(stderr, "%s\n", mysql_error(con)); + mysql_close(con); + exit(1); +} + +int main(int argc, char **argv) +{ + MYSQL *con = NULL; + MYSQL_RES *result = NULL; + int num_fields = 0; + MYSQL_ROW row; + + printf("MySQL client version: %s\n", mysql_get_client_info()); + + con = mysql_init(NULL); + if (con == NULL) { + finish_with_error(con); + } + + if (mysql_real_connect(con, "127.0.0.1", "root", "", "mydb", 3306, NULL, 0) == NULL) { + finish_with_error(con); + } + + if (mysql_query(con, "SELECT name, email, phone_numbers FROM mytable")) { + finish_with_error(con); + } + + result = mysql_store_result(con); + if (result == NULL) { + finish_with_error(con); + } + + num_fields = mysql_num_fields(result); + while ((row = mysql_fetch_row(result))) { + for(int i = 0; i < num_fields; i++) { + printf("%s ", row[i] ? row[i] : "NULL"); + } + printf("\n"); + } + + mysql_free_result(result); + mysql_close(con); + + return 0; +} +``` \ No newline at end of file diff --git a/docs/using-gitbase/supported-syntax.md b/docs/using-gitbase/supported-syntax.md index 4019b194f..84d33a0e0 100644 --- a/docs/using-gitbase/supported-syntax.md +++ b/docs/using-gitbase/supported-syntax.md @@ -1,3 +1,123 @@ -## Supported syntax +# Supported SQL Syntax -To see the SQL subset currently supported take a look at [this list](https://github.com/src-d/go-mysql-server/blob/3dd441325d1731821eff0495fbf63747c258b8ff/SUPPORTED.md) from [src-d/go-mysql-server](https://github.com/src-d/go-mysql-server). +## Comparisson expressions +- != +- == +- \> +- < +- \>= +- <= +- BETWEEN +- IN +- NOT IN +- REGEXP + +## Null check expressions +- IS NOT NULL +- IS NULL + +## Grouping expressions +- AVG +- COUNT +- MAX +- MIN +- SUM (always returns DOUBLE) + +## Standard expressions +- ALIAS (AS) +- CAST/CONVERT +- CREATE TABLE +- DESCRIBE/DESC/EXPLAIN [table name] +- DESCRIBE/DESC/EXPLAIN FORMAT=TREE [query] +- DISTINCT +- FILTER (WHERE) +- GROUP BY +- INSERT INTO +- LIMIT/OFFSET +- LITERAL +- ORDER BY +- SELECT +- SHOW TABLES +- SORT +- STAR (*) +- SHOW PROCESSLIST +- SHOW TABLE STATUS +- SHOW VARIABLES +- SHOW CREATE DATABASE +- SHOW CREATE TABLE +- SHOW FIELDS FROM +- LOCK/UNLOCK +- USE +- SHOW DATABASES +- SHOW WARNINGS + +## Index expressions +- CREATE INDEX (an index can be created using either column names or a single arbitrary expression). +- DROP INDEX +- SHOW {INDEXES | INDEX | KEYS} {FROM | IN} [table name] + +## Join expressions +- CROSS JOIN +- INNER JOIN +- NATURAL JOIN + +## Logical expressions +- AND +- NOT +- OR + +## Arithmetic expressions +- \+ +- \- +- \* +- \\ +- << +- \>> +- & +- \| +- ^ +- div +- % + +## Subqueries +- supported only as tables, not as expressions. + +## Functions +- ARRAY_LENGTH +- CONCAT +- CONCAT_WS +- IS_BINARY +- SPLIT +- SUBSTRING +- IS_BINARY +- LOWER +- UPPER +- CEILING +- CEIL +- FLOOR +- ROUND +- COALESCE +- CONNECTION_ID +- SOUNDEX +- JSON_EXTRACT +- DATABASE +- SQRT +- POW +- POWER +- RPAD +- LPAD +- LN +- LOG2 +- LOG10 + +## Time functions +- DAY +- WEEKDAY +- DAYOFWEEK +- DAYOFYEAR +- HOUR +- MINUTE +- MONTH +- SECOND +- YEAR +- NOW diff --git a/vendor/gopkg.in/src-d/go-mysql-server.v0/.gitignore b/vendor/gopkg.in/src-d/go-mysql-server.v0/.gitignore new file mode 100644 index 000000000..251b391c1 --- /dev/null +++ b/vendor/gopkg.in/src-d/go-mysql-server.v0/.gitignore @@ -0,0 +1,32 @@ +# 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 + +# Tpc-h tbl files +benchmark/*tbl +vendor +Makefile.main +.ci/ +_example/main +_example/*.exe diff --git a/vendor/gopkg.in/src-d/go-mysql-server.v0/.travis.yml b/vendor/gopkg.in/src-d/go-mysql-server.v0/.travis.yml new file mode 100644 index 000000000..d79357199 --- /dev/null +++ b/vendor/gopkg.in/src-d/go-mysql-server.v0/.travis.yml @@ -0,0 +1,106 @@ +language: go +go_import_path: gopkg.in/src-d/go-mysql-server.v0 + +env: + global: + - LD_LIBRARY_PATH="/usr/local/lib":${LD_LIBRARY_PATH} + +addons: + apt: + packages: + - libonig-dev + - libmysqlclient-dev + +matrix: + fast_finish: true + +sudo: required + +services: + - docker + +install: + - go get -u github.com/golang/dep/cmd/dep + - touch Gopkg.toml + - dep ensure -v -add "github.com/pilosa/pilosa@f62dbc00b96f596a1f2ef8b4e87ba8ec847eda37" "github.com/moovweb/rubex@b3d9ff6ad7d9b14f94a91c8271cd9ad9e77132e5" + - make dependencies + +before_script: + - sudo service mysql stop + +script: + - make ci-script + +jobs: + include: + - go: 1.10.x + - go: 1.11.x + + # Integration test builds for 3rd party clients + - go: 1.11.x + script: + - make TEST=go integration + + - language: python + python: '3.6' + before_install: + - eval "$(gimme 1.11.2)" + install: + - GO111MODULE=on go get ./... + script: + - make TEST=python-pymysql integration + + - language: php + php: '7.1' + before_install: + - eval "$(gimme 1.11.2)" + install: + - GO111MODULE=on go get ./... + script: + - make TEST=php integration + + - language: ruby + ruby: '2.3' + before_install: + - eval "$(gimme 1.11.2)" + install: + - GO111MODULE=on go get ./... + script: + - make TEST=ruby integration + + - language: java + jdk: openjdk10 + before_install: + - eval "$(gimme 1.11.2)" + install: + - GO111MODULE=on go get ./... + script: + - make TEST=jdbc-mariadb integration + + - language: node_js + node_js: '7' + before_install: + - eval "$(gimme 1.11.2)" + install: + - GO111MODULE=on go get ./... + script: + - make TEST=javascript integration + + - language: csharp + mono: none + dotnet: '2.1' + before_install: + - eval "$(gimme 1.11.2)" + install: + - GO111MODULE=on go get ./... + script: + - make TEST=dotnet integration + + - language: c + compiler: clang + before_install: + - eval "$(gimme 1.11.2)" + install: + - GO111MODULE=on go get ./... + script: + - make TEST=c integration \ No newline at end of file diff --git a/vendor/gopkg.in/src-d/go-mysql-server.v0/ARCHITECTURE.md b/vendor/gopkg.in/src-d/go-mysql-server.v0/ARCHITECTURE.md new file mode 100644 index 000000000..cfd94d65c --- /dev/null +++ b/vendor/gopkg.in/src-d/go-mysql-server.v0/ARCHITECTURE.md @@ -0,0 +1,137 @@ +# Architecture overview + +This document provides an overview of all parts and pieces of the project as well as how they fit together. It is meant to help new contributors understand where things may be, and how changes in some components may interact with other components of the system. + +## Root package (`sqle`) + +This is where the engine lives. The engine is the piece that coordinates and makes all other pieces work together as well as the main API users of the system will use to create and configure an engine and perform queries. + +Because this is the point where all components fit together, it is also where integration tests are. Those integration tests can be found in `engine_test.go`. +A test should be added here, plus in any specific place where the feature/issue belonged, if needed. + +**How to add integration tests** + +The test executing all integration test is `TestQueries`, which you can run with the following command: + +``` +go test -run=TestQueries +``` + +This test is just executing all the queries in a loop. New test cases should be added to the `queries` package variable at the top of `engine_test.go`. +Simply add a new element to the slice with the query and the expected result. + +## `sql` + +This package is probably the most important of the project. It has several main roles: +- Defines the main interfaces used in the rest of the packages `Node`, `Expression`, ... +- Provides implementations of components used in the rest of the packages `Row`, `Context`, `ProcessList`, `Catalog`, ... +- Defines the `information_schema` table, which is a special table available in all databases and contains some data about the schemas of other tables. + +### `sql/analyzer` + +The analyzer is the more complex component of the project. It contains a main component, which is the `Analyzer`, in charge of executing its registered rules on execution trees for resolving some parts, removing redundant data, optimizing things for performance, etc. + +There are several phases on the analyzer, because some rules need to be run before others, some need to be executed several times, other just once, etc. +Inside `rules.go` are all the default rules and the phases in which they're executed. + +On top of that, all available rules are defined in this package. Each rule has a specific role in the analyzer. Rules should be as small and atomic as possible and try to do only one job and always produce a tree that is as resolved as the one it received or more. + +### `sql/expression` + +This package includes the implementation of all the SQL expressions available in go-mysql-server, except functions. Arithmetic operators, logic operators, conversions, etc are implemented here. + +Inside `registry.go` there is a registry of all the default functions, even if they're not defined here. + +`Inspect` and `Walk` utility functions are provided to inspect expressions. + +### `sql/expression/function` + +Implementation of all the functions available in go-mysql-server. + +### `sql/expression/function/aggregation` + +Implementation of all the aggregation functions available in go-mysql-server. + +### `sql/index` + +Contains the index driver configuration file implementation and other utilities for dealing with index drivers. + +### `sql/index/pilosa` + +Actual implementation of an index driver. Underneath, it's using a bitmap database called pilosa (hence the name) to implement bitmap indexes. + +### `sql/parse` + +This package exposes the `Parse` function, which parses a SQL query and translates it into an execution plan. + +Parsing is done using `vitess` parser, but sometimes there are queries vitess cannot parse. In this case, custom parsers are used. Otherwise, vitess is used to parse the query and then converted to a go-mysql-server execution plan. + +### `sql/plan` + +All the different nodes of the execution plan (except for very specific nodes used in some optimisation rules) are defined here. + +For example, `SELECT foo FROM bar` is translated into the following plan: + +``` +Project(foo) + |- Table(bar) +``` + +Which means, the execution plan is a `Project` node projecting `foo` and has a `ResolvedTable`, which is `bar` as its children. + +Each node inside this package implements at least the `sql.Node` interface, but it can implement more. `sql.Expressioner`, for example. + +Along with the nodes, `Inspect` and `Walk` functions are provided as utilities to inspect an execution tree. + +## `server` + +Contains all the code to turn an engine into a runnable server that can communicate using the MySQL wire protocol. + +## `auth` + +This package contains all the code related to the audit log, authentication and permission management in go-mysql-server. + +There are two authentication methods: +- **None:** no authentication needed. +- **Native:** authentication performed with user and password. Read, write or all permissions can be specified for those users. It can also be configured using a JSON file. + +## `internal/regex` + +go-mysql-server has multiple regular expression engines, such as oniguruma and the standard Go regexp engine. In this package, a common interface for regular expression engines is defined. +This means, Go standard library `regexp` package should not be used in any user-facing feature, instead this package should be used. + +The default engine is oniguruma, but the Go standard library engine can be used using the `mysql_go_regex` build tag. + +## `test` + +Test contains pieces that are only used for tests, such as an opentracing tracer that stores spans in memory to be inspected later in the tests. + +## `_integration` + +To ensure compatibility with some clients, there is a small example connecting and querying a go-mysql-server server from those clients. Each folder corresponds to a different client. + +For more info about supported clients see [SUPPORTED_CLIENTS.md](/SUPPORTED_CLIENTS.md). + +These integrations tests can be run using this command: + +``` +make TEST=${CLIENT FOLDER NAME} integration +``` + +It will take care of setting up the test server and shutting it down. + +## `_example` + +A small example of how to use go-mysql-server to create a server and run it. + +# Connecting the dots + +`server` uses the engine defined in `sql`. + +Engine uses audit logs and authentication defined in `auth`, parses using `sql/parse` to convert a query into an execution plan, with nodes defined in `sql/plan` and expressions defined in `sql/expression`, `sql/expression/function` and `sql/expression/function/aggregation`. + +After parsing, the obtained execution plan is analyzed using the analyzer defined in `sql/analyzer` and its rules to resolve tables, fields, databases, apply optimisation rules, etc. + +If indexes can be used, the analyzer will transform the query so it uses indexes reading from the drivers in `sql/index` (in this case `sql/index/pilosa` because there is only one driver). + +Once the plan is analyzed, it will be executed recursively from the top of the tree to the bottom to obtain the results and they will be sent back to the client using the MySQL wire protocol. \ No newline at end of file diff --git a/vendor/gopkg.in/src-d/go-mysql-server.v0/CONTRIBUTING.md b/vendor/gopkg.in/src-d/go-mysql-server.v0/CONTRIBUTING.md new file mode 100644 index 000000000..71a98af05 --- /dev/null +++ b/vendor/gopkg.in/src-d/go-mysql-server.v0/CONTRIBUTING.md @@ -0,0 +1,62 @@ +# source{d} Contributing Guidelines + +source{d} projects accept contributions via GitHub pull requests. +This document outlines some of the +conventions on development workflow, commit message formatting, contact points, +and other resources to make it easier to get your contribution accepted. + +## Certificate of Origin + +By contributing to this project you agree to the [Developer Certificate of +Origin (DCO)](DCO). This document was created by the Linux Kernel community and is a +simple statement that you, as a contributor, have the legal right to make the +contribution. + +In order to show your agreement with the DCO you should include at the end of commit message, +the following line: `Signed-off-by: John Doe `, using your real name. + +This can be done easily using the [`-s`](https://github.com/git/git/blob/b2c150d3aa82f6583b9aadfecc5f8fa1c74aca09/Documentation/git-commit.txt#L154-L161) flag on the `git commit`. + +## Support Channels + +The official support channels, for both users and contributors, are: + +- GitHub issues: each repository has its own list of issues. +- Slack: join the [source{d} Slack](https://join.slack.com/t/sourced-community/shared_invite/enQtMjc4Njk5MzEyNzM2LTFjNzY4NjEwZGEwMzRiNTM4MzRlMzQ4MmIzZjkwZmZlM2NjODUxZmJjNDI1OTcxNDAyMmZlNmFjODZlNTg0YWM) community. + +*Before opening a new issue or submitting a new pull request, it's helpful to +search the project - it's likely that another user has already reported the +issue you're facing, or it's a known issue that we're already aware of. + +## How to Contribute + +Pull Requests (PRs) are the main and exclusive way to contribute code to source{d} projects. +In order for a PR to be accepted it needs to pass a list of requirements: + +- The contribution must be correctly explained with natural language and providing a minimum working example that reproduces it. +- All PRs must be written idiomatically: + - for Go: formatted according to [gofmt](https://golang.org/cmd/gofmt/), and without any warnings from [go lint](https://github.com/golang/lint) nor [go vet](https://golang.org/cmd/vet/) + - for other languages, similar constraints apply. +- They should in general include tests, and those shall pass. + - If the PR is a bug fix, it has to include a new unit test that fails before the patch is merged. + - If the PR is a new feature, it has to come with a suite of unit tests, that tests the new functionality. + - In any case, all the PRs have to pass the personal evaluation of at least one of the [maintainers](MAINTAINERS) of the project. + +### Getting started + +If you are a new contributor to the project, reading [ARCHITECTURE.md](/ARCHITECTURE.md) is highly recommended, as it contains all the details about the architecture of go-mysql-server and its components. + + +### Format of the commit message + +Every commit message should describe what was changed, under which context and, if applicable, the GitHub issue it relates to: + +``` +plumbing: packp, Skip argument validations for unknown capabilities. Fixes #623 +``` + +The format can be described more formally as follows: + +``` +: , . [Fixes #] +``` diff --git a/vendor/gopkg.in/src-d/go-mysql-server.v0/DCO b/vendor/gopkg.in/src-d/go-mysql-server.v0/DCO new file mode 100644 index 000000000..716561d5d --- /dev/null +++ b/vendor/gopkg.in/src-d/go-mysql-server.v0/DCO @@ -0,0 +1,36 @@ +Developer Certificate of Origin +Version 1.1 + +Copyright (C) 2004, 2006 The Linux Foundation and its contributors. +660 York Street, Suite 102, +San Francisco, CA 94110 USA + +Everyone is permitted to copy and distribute verbatim copies of this +license document, but changing it is not allowed. + + +Developer's Certificate of Origin 1.1 + +By making a contribution to this project, I certify that: + +(a) The contribution was created in whole or in part by me and I + have the right to submit it under the open source license + indicated in the file; or + +(b) The contribution is based upon previous work that, to the best + of my knowledge, is covered under an appropriate open source + license and I have the right under that license to submit that + work with modifications, whether created in whole or in part + by me, under the same open source license (unless I am + permitted to submit under a different license), as indicated + in the file; or + +(c) The contribution was provided directly to me by some other + person who certified (a), (b) or (c) and I have not modified + it. + +(d) I understand and agree that this project and the contribution + are public and that a record of the contribution (including all + personal information I submit with it, including my sign-off) is + maintained indefinitely and may be redistributed consistent with + this project or the open source license(s) involved. diff --git a/vendor/gopkg.in/src-d/go-mysql-server.v0/MAINTAINERS b/vendor/gopkg.in/src-d/go-mysql-server.v0/MAINTAINERS new file mode 100644 index 000000000..dc6817245 --- /dev/null +++ b/vendor/gopkg.in/src-d/go-mysql-server.v0/MAINTAINERS @@ -0,0 +1 @@ +Antonio Navarro Perez (@ajnavarro) diff --git a/vendor/gopkg.in/src-d/go-mysql-server.v0/Makefile b/vendor/gopkg.in/src-d/go-mysql-server.v0/Makefile new file mode 100644 index 000000000..81c0e52e0 --- /dev/null +++ b/vendor/gopkg.in/src-d/go-mysql-server.v0/Makefile @@ -0,0 +1,27 @@ +# Package configuration +PROJECT = go-mysql-server +COMMANDS = +UNAME_S := $(shell uname -s) + +# Including ci Makefile +CI_REPOSITORY ?= https://github.com/src-d/ci.git +CI_BRANCH ?= v1 +CI_PATH ?= .ci +MAKEFILE := $(CI_PATH)/Makefile.main +$(MAKEFILE): + git clone --quiet --depth 1 -b $(CI_BRANCH) $(CI_REPOSITORY) $(CI_PATH); +-include $(MAKEFILE) + +integration: + ./_integration/run ${TEST} + +oniguruma: +ifeq ($(UNAME_S),Linux) + $(shell apt-get install libonig-dev) +endif + +ifeq ($(UNAME_S),Darwin) + $(shell brew install oniguruma) +endif + +.PHONY: integration \ No newline at end of file diff --git a/vendor/gopkg.in/src-d/go-mysql-server.v0/README.md b/vendor/gopkg.in/src-d/go-mysql-server.v0/README.md new file mode 100644 index 000000000..bc09e3f53 --- /dev/null +++ b/vendor/gopkg.in/src-d/go-mysql-server.v0/README.md @@ -0,0 +1,302 @@ +# go-mysql-server +[![License](https://img.shields.io/badge/License-Apache%202.0-blue.svg)](https://opensource.org/licenses/Apache-2.0) +Build Status +codecov +GoDoc +[![Issues](http://img.shields.io/github/issues/src-d/go-mysql-server.svg)](https://github.com/src-d/go-mysql-server/issues) + +**go-mysql-server** is a SQL engine which parses standard SQL (based on MySQL syntax), resolves and optimizes queries. +It provides simple interfaces to allow custom tabular data source implementations. + +**go-mysql-server** also provides a server implementation compatible with the MySQL wire protocol. +That means it is compatible with MySQL ODBC, JDBC, or the default MySQL client shell interface. + +## Scope of this project + +These are the goals of **go-mysql-server**: + +- Be a generic extensible SQL engine that performs queries on your data sources. +- Provide interfaces so you can implement your own custom data sources without providing any (except for the `mem` data source that is used for testing purposes). +- Have a runnable server you can use on your specific implementation. +- Parse and optimize queries while still allow specific implementations to add their own analysis steps and optimizations. +- Provide some common index driver implementations so the user does not have to bring their own index implementation, and still be able to do so if they need to. + +What are not the goals of **go-mysql-server**: + +- Be a drop-in MySQL database replacement. +- Be an application/server you can use directly. +- Provide any kind of backend implementation (other than the `mem` one used for testing) such as json, csv, yaml, ... That's for clients to implement and use. + +What's the use case of **go-mysql-server**? + +Having data in another format that you want as tabular data to query using SQL, such as git. As an example of this, we have [gitbase](https://github.com/src-d/gitbase). + +## Installation + +The import path for the package is `gopkg.in/src-d/go-mysql-server.v0`. + +To install it, run: + +``` +go get gopkg.in/src-d/go-mysql-server.v0 +``` + +## Documentation + +* [go-mysql-server godoc](https://godoc.org/github.com/src-d/go-mysql-server) + + +## SQL syntax + +We are continuously adding more functionality to go-mysql-server. We support a subset of what is supported in MySQL, to see what is currently included check the [SUPPORTED](./SUPPORTED.md) file. + +## Third-party clients + +We support and actively test against certain third-party clients to ensure compatibility between them and go-mysql-server. You can check out the list of supported third party clients in the [SUPPORTED_CLIENTS](./SUPPORTED_CLIENTS.md) file along with some examples on how to connect to go-mysql-server using them. + +## Available functions + + +| Name | Description | +|:-------------|:-------------------------------------------------------------------------------------------------------------------------------| +|`ARRAY_LENGTH(json)`|If the json representation is an array, this function returns its size.| +|`AVG(expr)`|Returns the average value of expr in all rows.| +|`CEIL(number)`|Return the smallest integer value that is greater than or equal to `number`.| +|`CEILING(number)`|Return the smallest integer value that is greater than or equal to `number`.| +|`COALESCE(...)`|The function returns the first non-null value in a list.| +|`CONCAT(...)`|Concatenate any group of fields into a single string.| +|`CONCAT_WS(sep, ...)`|Concatenate any group of fields into a single string. The first argument is the separator for the rest of the arguments. The separator is added between the strings to be concatenated. The separator can be a string, as can the rest of the arguments. If the separator is NULL, the result is NULL.| +|`CONNECTION_ID()`|Return the current connection ID.| +|`COUNT(expr)`| Returns a count of the number of non-NULL values of expr in the rows retrieved by a SELECT statement.| +|`DAY(date)`|Returns the day of the given date.| +|`DAYOFWEEK(date)`|Returns the day of the week of the given date.| +|`DAYOFYEAR(date)`|Returns the day of the year of the given date.| +|`FLOOR(number)`|Return the largest integer value that is less than or equal to `number`.| +|`HOUR(date)`|Returns the hours of the given date.| +|`IFNULL(expr1, expr2)`|If expr1 is not NULL, IFNULL() returns expr1; otherwise it returns expr2.| +|`IS_BINARY(blob)`|Returns whether a BLOB is a binary file or not.| +|`JSON_EXTRACT(json_doc, path, ...)`|Extracts data from a json document using json paths.| +|`LN(X)`|Return the natural logarithm of X.| +|`LOG(X), LOG(B, X)`|If called with one parameter, this function returns the natural logarithm of X. If called with two parameters, this function returns the logarithm of X to the base B. If X is less than or equal to 0, or if B is less than or equal to 1, then NULL is returned.| +|`LOG10(X)`|Returns the base-10 logarithm of X.| +|`LOG2(X)`|Returns the base-2 logarithm of X.| +|`LOWER(str)`|Returns the string str with all characters in lower case.| +|`LPAD(str, len, padstr)`|Return the string argument, left-padded with the specified string.| +|`LTRIM(str)`|Returns the string str with leading space characters removed.| +|`MAX(expr)`|Returns the maximum value of expr in all rows.| +|`MID(str, pos, [len])`|Return a substring from the provided string starting at `pos` with a length of `len` characters. If no `len` is provided, all characters from `pos` until the end will be taken.| +|`MIN(expr)`|Returns the minimum value of expr in all rows.| +|`MINUTE(date)`|Returns the minutes of the given date.| +|`MONTH(date)`|Returns the month of the given date.| +|`NOW()`|Returns the current timestamp.| +|`NULLIF(expr1, expr2)`|Returns NULL if expr1 = expr2 is true, otherwise returns expr1.| +|`POW(X, Y)`|Returns the value of X raised to the power of Y.| +|`REPEAT(str, count)`|Returns a string consisting of the string str repeated count times.| +|`REPLACE(str,from_str,to_str)`|Returns the string str with all occurrences of the string from_str replaced by the string to_str.| +|`REVERSE(str)`|Returns the string str with the order of the characters reversed.| +|`ROUND(number, decimals)`|Round the `number` to `decimals` decimal places.| +|`RPAD(str, len, padstr)`|Returns the string str, right-padded with the string padstr to a length of len characters.| +|`RTRIM(str)`|Returns the string str with trailing space characters removed.| +|`SECOND(date)`|Returns the seconds of the given date.| +|`SOUNDEX(str)`|Returns the soundex of a string.| +|`SPLIT(str,sep)`|Receives a string and a separator and returns the parts of the string split by the separator as a JSON array of strings.| +|`SQRT(X)`|Returns the square root of a nonnegative number X.| +|`SUBSTR(str, pos, [len])`|Return a substring from the provided string starting at `pos` with a length of `len` characters. If no `len` is provided, all characters from `pos` until the end will be taken.| +|`SUBSTRING(str, pos, [len])`|Return a substring from the provided string starting at `pos` with a length of `len` characters. If no `len` is provided, all characters from `pos` until the end will be taken.| +|`SUM(expr)`|Returns the sum of expr in all rows.| +|`TRIM(str)`|Returns the string str with all spaces removed.| +|`UPPER(str)`|Returns the string str with all characters in upper case.| +|`WEEKDAY(date)`|Returns the weekday of the given date.| +|`YEAR(date)`|Returns the year of the given date.| + + +## Configuration + +The behaviour of certain parts of go-mysql-server can be configured using either environment variables or session variables. + +Session variables are set using the following SQL queries: + +```sql +SET = +``` + + +| Name | Type | Description | +|:-----|:-----|:------------| +|`INMEMORY_JOINS`|environment|If set it will perform all joins in memory. Default is off.| +|`inmemory_joins`|session|If set it will perform all joins in memory. Default is off. This has precedence over `INMEMORY_JOINS`.| +|`MAX_MEMORY_INNER_JOIN`|environment|The maximum number of memory, in megabytes, that can be consumed by go-mysql-server before switching to multipass mode in inner joins. Default is the 20% of all available physical memory.| +|`max_memory_joins`|session|The maximum number of memory, in megabytes, that can be consumed by go-mysql-server before switching to multipass mode in inner joins. Default is the 20% of all available physical memory. This has precedence over `MAX_MEMORY_INNER_JOIN`.| +|`DEBUG_ANALYZER`|environment|If set, the analyzer will print debug messages. Default is off.| +|`PILOSA_INDEX_THREADS`|environment|Number of threads used in index creation. Default is the number of cores available in the machine.| +|`pilosa_index_threads`|environment|Number of threads used in index creation. Default is the number of cores available in the machine. This has precedence over `PILOSA_INDEX_THREADS`.| + + +## Example + +`go-mysql-server` contains a SQL engine and server implementation. So, if you want to start a server, first instantiate the engine and pass your `sql.Database` implementation. + +It will be in charge of handling all the logic to retrieve the data from your source. +Here you can see an example using the in-memory database implementation: + +```go +... + +func main() { + driver := sqle.NewDefault() + driver.AddDatabase(createTestDatabase()) + + config := server.Config{ + Protocol: "tcp", + Address: "localhost:3306", + Auth: auth.NewNativeSingle("user", "pass", auth.AllPermissions), + } + + s, err := server.NewDefaultServer(config, driver) + if err != nil { + panic(err) + } + + s.Start() +} + +func createTestDatabase() *mem.Database { + const ( + dbName = "test" + tableName = "mytable" + ) + + db := mem.NewDatabase(dbName) + table := mem.NewTable(tableName, sql.Schema{ + {Name: "name", Type: sql.Text, Nullable: false, Source: tableName}, + {Name: "email", Type: sql.Text, Nullable: false, Source: tableName}, + {Name: "phone_numbers", Type: sql.JSON, Nullable: false, Source: tableName}, + {Name: "created_at", Type: sql.Timestamp, Nullable: false, Source: tableName}, + }) + + db.AddTable(tableName, table) + ctx := sql.NewEmptyContext() + + rows := []sql.Row{ + sql.NewRow("John Doe", "john@doe.com", []string{"555-555-555"}, time.Now()), + sql.NewRow("John Doe", "johnalt@doe.com", []string{}, time.Now()), + sql.NewRow("Jane Doe", "jane@doe.com", []string{}, time.Now()), + sql.NewRow("Evil Bob", "evilbob@gmail.com", []string{"555-666-555", "666-666-666"}, time.Now()), + } + + for _, row := range rows { + table.Insert(ctx, row) + } + + return db +} + +... +``` + +Then, you can connect to the server with any MySQL client: +```bash +> mysql --host=127.0.0.1 --port=3306 -u user -ppass test -e "SELECT * FROM mytable" ++----------+-------------------+-------------------------------+---------------------+ +| name | email | phone_numbers | created_at | ++----------+-------------------+-------------------------------+---------------------+ +| John Doe | john@doe.com | ["555-555-555"] | 2018-04-18 10:42:58 | +| John Doe | johnalt@doe.com | [] | 2018-04-18 10:42:58 | +| Jane Doe | jane@doe.com | [] | 2018-04-18 10:42:58 | +| Evil Bob | evilbob@gmail.com | ["555-666-555","666-666-666"] | 2018-04-18 10:42:58 | ++----------+-------------------+-------------------------------+---------------------+ +``` + +See the complete example [here](_example/main.go). + +### Queries examples + +``` +SELECT count(name) FROM mytable ++---------------------+ +| COUNT(mytable.name) | ++---------------------+ +| 4 | ++---------------------+ + +SELECT name,year(created_at) FROM mytable ++----------+--------------------------+ +| name | YEAR(mytable.created_at) | ++----------+--------------------------+ +| John Doe | 2018 | +| John Doe | 2018 | +| Jane Doe | 2018 | +| Evil Bob | 2018 | ++----------+--------------------------+ + +SELECT email FROM mytable WHERE name = 'Evil Bob' ++-------------------+ +| email | ++-------------------+ +| evilbob@gmail.com | ++-------------------+ +``` + +## Custom data source implementation + +To be able to create your own data source implementation you need to implement the following interfaces: + +- `sql.Database` interface. This interface will provide tables from your data source. + - If your database implementation supports adding more tables, you might want to add support for `sql.Alterable` interface + +- `sql.Table` interface. It will be in charge of transforming any kind of data into an iterator of Rows. Depending on how much you want to optimize the queries, you also can implement other interfaces on your tables: + - `sql.PushdownProjectionTable` interface will provide a way to get only the columns needed for the executed query. + - `sql.PushdownProjectionAndFiltersTable` interface will provide the same functionality described before, but also will push down the filters used in the executed query. It allows to filter data in advance, and speed up queries. + - `sql.Indexable` add index capabilities to your table. By implementing this interface you can create and use indexes on this table. + - `sql.Inserter` can be implemented if your data source tables allow insertions. + +- If you need some custom tree modifications, you can also implement your own `analyzer.Rules`. + +You can see a really simple data source implementation on our `mem` package. + +## Indexes + +`go-mysql-server` exposes a series of interfaces to allow you to implement your own indexes so you can speedup your queries. + +Taking a look at the main [index interface](https://github.com/src-d/go-mysql-server/blob/master/sql/index.go#L35), you must note a couple of constraints: + +- This abstraction lets you create an index for multiple columns (one or more) or for **only one** expression (e.g. function applied on multiple columns). +- If you want to index an expression that is not a column you will only be able to index **one and only one** expression at a time. + +## Custom index driver implementation + +Index drivers provide different backends for storing and querying indexes. To implement a custom index driver you need to implement a few things: + +- `sql.IndexDriver` interface, which will be the driver itself. Not that your driver must return an unique ID in the `ID` method. This ID is unique for your driver and should not clash with any other registered driver. It's the driver's responsibility to be fault tolerant and be able to automatically detect and recover from corruption in indexes. +- `sql.Index` interface, returned by your driver when an index is loaded or created. + - Your `sql.Index` may optionally implement the `sql.AscendIndex` and/or `sql.DescendIndex` interfaces, if you want to support more comparison operators like `>`, `<`, `>=`, `<=` or `BETWEEN`. +- `sql.IndexLookup` interface, returned by your index in any of the implemented operations to get a subset of the indexed values. + - Your `sql.IndexLookup` may optionally implement the `sql.Mergeable` and `sql.SetOperations` interfaces if you want to support set operations to merge your index lookups. +- `sql.IndexValueIter` interface, which will be returned by your `sql.IndexLookup` and should return the values of the index. +- Don't forget to register the index driver in your `sql.Catalog` using `catalog.RegisterIndexDriver(mydriver)` to be able to use it. + +To create indexes using your custom index driver you need to use `USING driverid` on the index creation query. For example: + +```sql +CREATE INDEX foo ON table USING driverid (col1, col2) +``` + +You can see an example of a driver implementation inside the `sql/index/pilosa` package, where the pilosa driver is implemented. + +Index creation is synchronous by default, to make it asynchronous, use `WITH (async = true)`, for example: + +```sql +CREATE INDEX foo ON table USING driverid (col1, col2) WITH (async = true) +``` + +### Old `pilosalib` driver + +`pilosalib` driver was renamed to `pilosa` and now `pilosa` does not require an external pilosa server. + +## Powered by go-mysql-server + +* [gitbase](https://github.com/src-d/gitbase) + +## License + +Apache License 2.0, see [LICENSE](/LICENSE) diff --git a/vendor/gopkg.in/src-d/go-mysql-server.v0/SUPPORTED.md b/vendor/gopkg.in/src-d/go-mysql-server.v0/SUPPORTED.md new file mode 100644 index 000000000..84d33a0e0 --- /dev/null +++ b/vendor/gopkg.in/src-d/go-mysql-server.v0/SUPPORTED.md @@ -0,0 +1,123 @@ +# Supported SQL Syntax + +## Comparisson expressions +- != +- == +- \> +- < +- \>= +- <= +- BETWEEN +- IN +- NOT IN +- REGEXP + +## Null check expressions +- IS NOT NULL +- IS NULL + +## Grouping expressions +- AVG +- COUNT +- MAX +- MIN +- SUM (always returns DOUBLE) + +## Standard expressions +- ALIAS (AS) +- CAST/CONVERT +- CREATE TABLE +- DESCRIBE/DESC/EXPLAIN [table name] +- DESCRIBE/DESC/EXPLAIN FORMAT=TREE [query] +- DISTINCT +- FILTER (WHERE) +- GROUP BY +- INSERT INTO +- LIMIT/OFFSET +- LITERAL +- ORDER BY +- SELECT +- SHOW TABLES +- SORT +- STAR (*) +- SHOW PROCESSLIST +- SHOW TABLE STATUS +- SHOW VARIABLES +- SHOW CREATE DATABASE +- SHOW CREATE TABLE +- SHOW FIELDS FROM +- LOCK/UNLOCK +- USE +- SHOW DATABASES +- SHOW WARNINGS + +## Index expressions +- CREATE INDEX (an index can be created using either column names or a single arbitrary expression). +- DROP INDEX +- SHOW {INDEXES | INDEX | KEYS} {FROM | IN} [table name] + +## Join expressions +- CROSS JOIN +- INNER JOIN +- NATURAL JOIN + +## Logical expressions +- AND +- NOT +- OR + +## Arithmetic expressions +- \+ +- \- +- \* +- \\ +- << +- \>> +- & +- \| +- ^ +- div +- % + +## Subqueries +- supported only as tables, not as expressions. + +## Functions +- ARRAY_LENGTH +- CONCAT +- CONCAT_WS +- IS_BINARY +- SPLIT +- SUBSTRING +- IS_BINARY +- LOWER +- UPPER +- CEILING +- CEIL +- FLOOR +- ROUND +- COALESCE +- CONNECTION_ID +- SOUNDEX +- JSON_EXTRACT +- DATABASE +- SQRT +- POW +- POWER +- RPAD +- LPAD +- LN +- LOG2 +- LOG10 + +## Time functions +- DAY +- WEEKDAY +- DAYOFWEEK +- DAYOFYEAR +- HOUR +- MINUTE +- MONTH +- SECOND +- YEAR +- NOW diff --git a/vendor/gopkg.in/src-d/go-mysql-server.v0/SUPPORTED_CLIENTS.md b/vendor/gopkg.in/src-d/go-mysql-server.v0/SUPPORTED_CLIENTS.md new file mode 100644 index 000000000..e30e928e9 --- /dev/null +++ b/vendor/gopkg.in/src-d/go-mysql-server.v0/SUPPORTED_CLIENTS.md @@ -0,0 +1,274 @@ +# Supported clients + +These are the clients we actively test against to check are compatible with go-mysql-server. Other clients may also work, but we don't check on every build if we remain compatible with them. + +- Python + - [pymysql](#pymysql) + - [mysql-connector](#python-mysql-connector) + - [sqlalchemy](#python-sqlalchemy) +- Ruby + - [ruby-mysql](#ruby-mysql) +- [PHP](#php) +- Node.js + - [mysqljs/mysql](#mysqljs) +- .NET Core + - [MysqlConnector](#mysqlconnector) +- Java/JVM + - [mariadb-java-client](#mariadb-java-client) +- Go + - [go-mysql-driver/mysql](#go-mysql-driver-mysql) +- C + - [mysql-connector-c](#mysql-connector-c) +- Grafana +- Tableau Desktop + +## Example client usage + +### pymysql + +```python +import pymysql.cursors + +connection = pymysql.connect(host='127.0.0.1', + user='root', + password='', + db='mydb', + cursorclass=pymysql.cursors.DictCursor) + +try: + with connection.cursor() as cursor: + sql = "SELECT * FROM mytable LIMIT 1" + cursor.execute(sql) + rows = cursor.fetchall() + + # use rows +finally: + connection.close() +``` + +### Python mysql-connector + +```python +import mysql.connector + +connection = mysql.connector.connect(host='127.0.0.1', + user='root', + passwd='', + port=3306, + database='mydb') + +try: + cursor = connection.cursor() + sql = "SELECT * FROM mytable LIMIT 1" + cursor.execute(sql) + rows = cursor.fetchall() + + # use rows +finally: + connection.close() +``` + +### Python sqlalchemy + +```python +import pandas as pd +import sqlalchemy + +engine = sqlalchemy.create_engine('mysql+pymysql://root:@127.0.0.1:3306/mydb') +with engine.connect() as conn: + repo_df = pd.read_sql_table("mytable", con=conn) + for table_name in repo_df.to_dict(): + print(table_name) +``` + +### ruby-mysql + +```ruby +require "mysql" + +conn = Mysql::new("127.0.0.1", "root", "", "mydb") +resp = conn.query "SELECT * FROM mytable LIMIT 1" + +# use resp + +conn.close() +``` + +### php + +```php +try { + $conn = new PDO("mysql:host=127.0.0.1:3306;dbname=mydb", "root", ""); + $conn->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); + + $stmt = $conn->query('SELECT * FROM mytable LIMIT 1'); + $result = $stmt->fetchAll(PDO::FETCH_ASSOC); + + // use result +} catch (PDOException $e) { + // handle error +} +``` + +### mysqljs + +```js +import mysql from 'mysql'; + +const connection = mysql.createConnection({ + host: '127.0.0.1', + port: 3306, + user: 'root', + password: '', + database: 'mydb' +}); +connection.connect(); + +const query = 'SELECT * FROM mytable LIMIT 1'; +connection.query(query, function (error, results, _) { + if (error) throw error; + + // use results +}); + +connection.end(); +``` + +### MysqlConnector + +```csharp +using MySql.Data.MySqlClient; +using System.Threading.Tasks; + +namespace something +{ + public class Something + { + public async Task DoQuery() + { + var connectionString = "server=127.0.0.1;user id=root;password=;port=3306;database=mydb;"; + + using (var conn = new MySqlConnection(connectionString)) + { + await conn.OpenAsync(); + + var sql = "SELECT * FROM mytable LIMIT 1"; + + using (var cmd = new MySqlCommand(sql, conn)) + using (var reader = await cmd.ExecuteReaderAsync()) + while (await reader.ReadAsync()) { + // use reader + } + } + } + } +} +``` + +### mariadb-java-client + +```java +package org.testing.mariadbjavaclient; + +import java.sql.*; + +class Main { + public static void main(String[] args) { + String dbUrl = "jdbc:mariadb://127.0.0.1:3306/mydb?user=root&password="; + String query = "SELECT * FROM mytable LIMIT 1"; + + try (Connection connection = DriverManager.getConnection(dbUrl)) { + try (PreparedStatement stmt = connection.prepareStatement(query)) { + try (ResultSet rs = stmt.executeQuery()) { + while (rs.next()) { + // use rs + } + } + } + } catch (SQLException e) { + // handle failure + } + } +} +``` + +### go-sql-driver/mysql + +```go +package main + +import ( + "database/sql" + + _ "github.com/go-sql-driver/mysql" +) + +func main() { + db, err := sql.Open("mysql", "root:@tcp(127.0.0.1:3306)/mydb") + if err != nil { + // handle error + } + + rows, err := db.Query("SELECT * FROM mytable LIMIT 1") + if err != nil { + // handle error + } + + // use rows +} +``` + +### #mysql-connector-c + +```c +#include +#include + +void finish_with_error(MYSQL *con) +{ + fprintf(stderr, "%s\n", mysql_error(con)); + mysql_close(con); + exit(1); +} + +int main(int argc, char **argv) +{ + MYSQL *con = NULL; + MYSQL_RES *result = NULL; + int num_fields = 0; + MYSQL_ROW row; + + printf("MySQL client version: %s\n", mysql_get_client_info()); + + con = mysql_init(NULL); + if (con == NULL) { + finish_with_error(con); + } + + if (mysql_real_connect(con, "127.0.0.1", "root", "", "mydb", 3306, NULL, 0) == NULL) { + finish_with_error(con); + } + + if (mysql_query(con, "SELECT name, email, phone_numbers FROM mytable")) { + finish_with_error(con); + } + + result = mysql_store_result(con); + if (result == NULL) { + finish_with_error(con); + } + + num_fields = mysql_num_fields(result); + while ((row = mysql_fetch_row(result))) { + for(int i = 0; i < num_fields; i++) { + printf("%s ", row[i] ? row[i] : "NULL"); + } + printf("\n"); + } + + mysql_free_result(result); + mysql_close(con); + + return 0; +} +``` \ No newline at end of file diff --git a/vendor/gopkg.in/src-d/go-mysql-server.v0/_config.yml b/vendor/gopkg.in/src-d/go-mysql-server.v0/_config.yml new file mode 100644 index 000000000..fc24e7a62 --- /dev/null +++ b/vendor/gopkg.in/src-d/go-mysql-server.v0/_config.yml @@ -0,0 +1 @@ +theme: jekyll-theme-hacker \ No newline at end of file diff --git a/vendor/gopkg.in/src-d/go-mysql-server.v0/engine.go b/vendor/gopkg.in/src-d/go-mysql-server.v0/engine.go index c8140105a..f42cf8fb6 100644 --- a/vendor/gopkg.in/src-d/go-mysql-server.v0/engine.go +++ b/vendor/gopkg.in/src-d/go-mysql-server.v0/engine.go @@ -34,9 +34,16 @@ func New(c *sql.Catalog, a *analyzer.Analyzer, cfg *Config) *Engine { versionPostfix = cfg.VersionPostfix } - c.RegisterFunctions(function.Defaults) - c.RegisterFunction("version", sql.FunctionN(function.NewVersion(versionPostfix))) - c.RegisterFunction("database", sql.Function0(function.NewDatabase(c))) + c.MustRegister( + sql.FunctionN{ + Name: "version", + Fn: function.NewVersion(versionPostfix), + }, + sql.Function0{ + Name: "database", + Fn: function.NewDatabase(c), + }) + c.MustRegister(function.Defaults...) // use auth.None if auth is not specified var au auth.Auth diff --git a/vendor/gopkg.in/src-d/go-mysql-server.v0/go.mod b/vendor/gopkg.in/src-d/go-mysql-server.v0/go.mod new file mode 100644 index 000000000..d249203c5 --- /dev/null +++ b/vendor/gopkg.in/src-d/go-mysql-server.v0/go.mod @@ -0,0 +1,41 @@ +module gopkg.in/src-d/go-mysql-server.v0 + +require ( + github.com/CAFxX/gcnotifier v0.0.0-20190112062741-224a280d589d // indirect + github.com/DataDog/datadog-go v0.0.0-20180822151419-281ae9f2d895 // indirect + github.com/StackExchange/wmi v0.0.0-20181212234831-e0a55b97c705 // indirect + github.com/boltdb/bolt v1.3.1 // indirect + github.com/cespare/xxhash v1.1.0 // indirect + github.com/codahale/hdrhistogram v0.0.0-20161010025455-3a0bb77429bd // indirect + github.com/go-ole/go-ole v1.2.4 // indirect + github.com/go-sql-driver/mysql v1.4.1 + github.com/gogo/protobuf v1.2.1 // indirect + github.com/golang/protobuf v1.3.0 // indirect + github.com/google/go-cmp v0.2.0 // indirect + github.com/gorilla/handlers v1.4.0 // indirect + github.com/gorilla/mux v1.7.0 // indirect + github.com/hashicorp/memberlist v0.1.3 // indirect + github.com/mitchellh/hashstructure v1.0.0 + github.com/oliveagle/jsonpath v0.0.0-20180606110733-2e52cf6e6852 + github.com/opentracing/opentracing-go v1.0.2 + github.com/pbnjay/memory v0.0.0-20190104145345-974d429e7ae4 + github.com/pelletier/go-toml v1.2.0 // indirect + github.com/pilosa/pilosa v1.2.0 + github.com/pkg/errors v0.8.1 // indirect + github.com/sanity-io/litter v1.1.0 + github.com/satori/go.uuid v1.2.0 // indirect + github.com/shirou/gopsutil v2.18.12+incompatible // indirect + github.com/shirou/w32 v0.0.0-20160930032740-bb4de0191aa4 // indirect + github.com/sirupsen/logrus v1.3.0 + github.com/spf13/cast v1.3.0 + github.com/src-d/go-oniguruma v1.0.0 + github.com/stretchr/testify v1.2.2 + github.com/uber/jaeger-client-go v2.15.0+incompatible // indirect + github.com/uber/jaeger-lib v2.0.0+incompatible // indirect + go.etcd.io/bbolt v1.3.2 + golang.org/x/net v0.0.0-20190227022144-312bce6e941f // indirect + google.golang.org/grpc v1.19.0 // indirect + gopkg.in/src-d/go-errors.v1 v1.0.0 + gopkg.in/src-d/go-vitess.v1 v1.5.0 + gopkg.in/yaml.v2 v2.2.2 +) diff --git a/vendor/gopkg.in/src-d/go-mysql-server.v0/go.sum b/vendor/gopkg.in/src-d/go-mysql-server.v0/go.sum new file mode 100644 index 000000000..ad84d2246 --- /dev/null +++ b/vendor/gopkg.in/src-d/go-mysql-server.v0/go.sum @@ -0,0 +1,144 @@ +cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= +github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ= +github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= +github.com/CAFxX/gcnotifier v0.0.0-20190112062741-224a280d589d h1:n0G4ckjMEj7bWuGYUX0i8YlBeBBJuZ+HEHvHfyBDZtI= +github.com/CAFxX/gcnotifier v0.0.0-20190112062741-224a280d589d/go.mod h1:Rn2zM2MnHze07LwkneP48TWt6UiZhzQTwCvw6djVGfE= +github.com/DataDog/datadog-go v0.0.0-20180822151419-281ae9f2d895 h1:dmc/C8bpE5VkQn65PNbbyACDC8xw8Hpp/NEurdPmQDQ= +github.com/DataDog/datadog-go v0.0.0-20180822151419-281ae9f2d895/go.mod h1:LButxg5PwREeZtORoXG3tL4fMGNddJ+vMq1mwgfaqoQ= +github.com/OneOfOne/xxhash v1.2.2 h1:KMrpdQIwFcEqXDklaen+P1axHaj9BSKzvpUUfnHldSE= +github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= +github.com/StackExchange/wmi v0.0.0-20181212234831-e0a55b97c705 h1:UUppSQnhf4Yc6xGxSkoQpPhb7RVzuv5Nb1mwJ5VId9s= +github.com/StackExchange/wmi v0.0.0-20181212234831-e0a55b97c705/go.mod h1:3eOhrUMpNV+6aFIbp5/iudMxNCF27Vw2OZgy4xEx0Fg= +github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da h1:8GUt8eRujhVEGZFFEjBj46YV4rDjvGrNxb0KMWYkL2I= +github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY= +github.com/boltdb/bolt v1.3.1 h1:JQmyP4ZBrce+ZQu0dY660FMfatumYDLun9hBCUVIkF4= +github.com/boltdb/bolt v1.3.1/go.mod h1:clJnj/oiGkjum5o1McbSZDSLxVThjynRyGBgiAx27Ps= +github.com/cespare/xxhash v1.1.0 h1:a6HrQnmkObjyL+Gs60czilIUGqrzKutQD6XZog3p+ko= +github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc= +github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= +github.com/codahale/hdrhistogram v0.0.0-20161010025455-3a0bb77429bd h1:qMd81Ts1T2OTKmB4acZcyKaMtRnY5Y44NuXGX2GFJ1w= +github.com/codahale/hdrhistogram v0.0.0-20161010025455-3a0bb77429bd/go.mod h1:sE/e/2PUdi/liOCUjSTXgM1o87ZssimdTWN964YiIeI= +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/go-ole/go-ole v1.2.4 h1:nNBDSCOigTSiarFpYE9J/KtEA1IOW4CNeqT9TQDqCxI= +github.com/go-ole/go-ole v1.2.4/go.mod h1:XCwSNxSkXRo4vlyPy93sltvi/qJq0jqQhjqQNIwKuxM= +github.com/go-sql-driver/mysql v1.4.1 h1:g24URVg0OFbNUTx9qqY1IRZ9D9z3iPyi5zKhQZpNwpA= +github.com/go-sql-driver/mysql v1.4.1/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w= +github.com/gogo/protobuf v1.2.1 h1:/s5zKNz0uPFCZ5hddgPdo2TK2TVrUNMn0OOX8/aZMTE= +github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4= +github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= +github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= +github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.0 h1:kbxbvI4Un1LUWKxufD+BiE6AEExYYgkQLQmLFqA1LFk= +github.com/golang/protobuf v1.3.0/go.mod h1:Qd/q+1AKNOZr9uGQzbzCmRO6sUih6GTPZv6a1/R87v0= +github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c h1:964Od4U6p2jUkFxvCydnIczKteheJEzHRToSGK3Bnlw= +github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= +github.com/google/go-cmp v0.2.0 h1:+dTQ8DZQJz0Mb/HjFlkptS1FeQ4cWSnN941F8aEG4SQ= +github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= +github.com/gorilla/handlers v1.4.0 h1:XulKRWSQK5uChr4pEgSE4Tc/OcmnU9GJuSwdog/tZsA= +github.com/gorilla/handlers v1.4.0/go.mod h1:Qkdc/uu4tH4g6mTK6auzZ766c4CA0Ng8+o/OAirnOIQ= +github.com/gorilla/mux v1.7.0 h1:tOSd0UKHQd6urX6ApfOn4XdBMY6Sh1MfxV3kmaazO+U= +github.com/gorilla/mux v1.7.0/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs= +github.com/hashicorp/errwrap v1.0.0 h1:hLrqtEDnRye3+sgx6z4qVLNuviH3MR5aQ0ykNJa/UYA= +github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= +github.com/hashicorp/go-immutable-radix v1.0.0 h1:AKDB1HM5PWEA7i4nhcpwOrO2byshxBjXVn/J/3+z5/0= +github.com/hashicorp/go-immutable-radix v1.0.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60= +github.com/hashicorp/go-msgpack v0.5.3 h1:zKjpN5BK/P5lMYrLmBHdBULWbJ0XpYR+7NGzqkZzoD4= +github.com/hashicorp/go-msgpack v0.5.3/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM= +github.com/hashicorp/go-multierror v1.0.0 h1:iVjPR7a6H0tWELX5NxNe7bYopibicUzc7uPribsnS6o= +github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk= +github.com/hashicorp/go-sockaddr v1.0.0 h1:GeH6tui99pF4NJgfnhp+L6+FfobzVW3Ah46sLo0ICXs= +github.com/hashicorp/go-sockaddr v1.0.0/go.mod h1:7Xibr9yA9JjQq1JpNB2Vw7kxv8xerXegt+ozgdvDeDU= +github.com/hashicorp/go-uuid v1.0.0 h1:RS8zrF7PhGwyNPOtxSClXXj9HA8feRnJzgnI1RJCSnM= +github.com/hashicorp/go-uuid v1.0.0/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= +github.com/hashicorp/golang-lru v0.5.0 h1:CL2msUPvZTLb5O648aiLNJw3hnBxN2+1Jq8rCOH9wdo= +github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= +github.com/hashicorp/memberlist v0.1.3 h1:EmmoJme1matNzb+hMpDuR/0sbJSUisxyqBGG676r31M= +github.com/hashicorp/memberlist v0.1.3/go.mod h1:ajVTdAv/9Im8oMAAj5G31PhhMCZJV2pPBoIllUwCN7I= +github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q= +github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= +github.com/konsorten/go-windows-terminal-sequences v1.0.1 h1:mweAR1A6xJ3oS2pRaGiHgQ4OO8tzTaLawm8vnODuwDk= +github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= +github.com/miekg/dns v1.0.14 h1:9jZdLNd/P4+SfEJ0TNyxYpsK8N4GtfylBLqtbYN1sbA= +github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg= +github.com/mitchellh/hashstructure v1.0.0 h1:ZkRJX1CyOoTkar7p/mLS5TZU4nJ1Rn/F8u9dGS02Q3Y= +github.com/mitchellh/hashstructure v1.0.0/go.mod h1:QjSHrPWS+BGUVBYkbTZWEnOh3G1DutKwClXU/ABz6AQ= +github.com/oliveagle/jsonpath v0.0.0-20180606110733-2e52cf6e6852 h1:Yl0tPBa8QPjGmesFh1D0rDy+q1Twx6FyU7VWHi8wZbI= +github.com/oliveagle/jsonpath v0.0.0-20180606110733-2e52cf6e6852/go.mod h1:eqOVx5Vwu4gd2mmMZvVZsgIqNSaW3xxRThUJ0k/TPk4= +github.com/opentracing/opentracing-go v1.0.2 h1:3jA2P6O1F9UOrWVpwrIo17pu01KWvNWg4X946/Y5Zwg= +github.com/opentracing/opentracing-go v1.0.2/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o= +github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c h1:Lgl0gzECD8GnQ5QCWA8o6BtfL6mDH5rQgM4/fX3avOs= +github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= +github.com/pbnjay/memory v0.0.0-20190104145345-974d429e7ae4 h1:MfIUBZ1bz7TgvQLVa/yPJZOGeKEgs6eTKUjz3zB4B+U= +github.com/pbnjay/memory v0.0.0-20190104145345-974d429e7ae4/go.mod h1:RMU2gJXhratVxBDTFeOdNhd540tG57lt9FIUV0YLvIQ= +github.com/pelletier/go-toml v1.2.0 h1:T5zMGML61Wp+FlcbWjRDT7yAxhJNAiPPLOFECq181zc= +github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= +github.com/pilosa/pilosa v1.2.0 h1:bi58fPsjyGIFnY5DtzECY+8RC9nRLvPrGC6fOqgtkTw= +github.com/pilosa/pilosa v1.2.0/go.mod h1:NgpkJkefqUKUHV7O3TqBOu89tsao3ksth2wzTNe8CPQ= +github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I= +github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/sanity-io/litter v1.1.0 h1:BllcKWa3VbZmOZbDCoszYLk7zCsKHz5Beossi8SUcTc= +github.com/sanity-io/litter v1.1.0/go.mod h1:CJ0VCw2q4qKU7LaQr3n7UOSHzgEMgcGco7N/SkZQPjw= +github.com/satori/go.uuid v1.2.0 h1:0uYX9dsZ2yD7q2RtLRtPSdGDWzjeM3TbMJP9utgA0ww= +github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0= +github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529 h1:nn5Wsu0esKSJiIVhscUtVbo7ada43DJhG55ua/hjS5I= +github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc= +github.com/shirou/gopsutil v2.18.12+incompatible h1:1eaJvGomDnH74/5cF4CTmTbLHAriGFsTZppLXDX93OM= +github.com/shirou/gopsutil v2.18.12+incompatible/go.mod h1:5b4v6he4MtMOwMlS0TUMTu2PcXUg8+E1lC7eC3UO/RA= +github.com/shirou/w32 v0.0.0-20160930032740-bb4de0191aa4 h1:udFKJ0aHUL60LboW/A+DfgoHVedieIzIXE8uylPue0U= +github.com/shirou/w32 v0.0.0-20160930032740-bb4de0191aa4/go.mod h1:qsXQc7+bwAM3Q1u/4XEfrquwF8Lw7D7y5cD8CuHnfIc= +github.com/sirupsen/logrus v1.3.0 h1:hI/7Q+DtNZ2kINb6qt/lS+IyXnHQe9e90POfeewL/ME= +github.com/sirupsen/logrus v1.3.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= +github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72 h1:qLC7fQah7D6K1B0ujays3HV9gkFtllcxhzImRR7ArPQ= +github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= +github.com/spf13/cast v1.3.0 h1:oget//CVOEoFewqQxwr0Ej5yjygnqGkvggSE/gB35Q8= +github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= +github.com/src-d/go-oniguruma v1.0.0 h1:JDk5PUAjreGsGAKLsoDLNmrsaryjJ5RqT3h+Si6aw/E= +github.com/src-d/go-oniguruma v1.0.0/go.mod h1:chVbff8kcVtmrhxtZ3yBVLLquXbzCS6DrxQaAK/CeqM= +github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/testify v1.2.2 h1:bSDNvY7ZPG5RlJ8otE/7V6gMiyenm9RtJ7IUVIAoJ1w= +github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= +github.com/uber/jaeger-client-go v2.15.0+incompatible h1:NP3qsSqNxh8VYr956ur1N/1C1PjvOJnJykCzcD5QHbk= +github.com/uber/jaeger-client-go v2.15.0+incompatible/go.mod h1:WVhlPFC8FDjOFMMWRy2pZqQJSXxYSwNYOkTr/Z6d3Kk= +github.com/uber/jaeger-lib v2.0.0+incompatible h1:iMSCV0rmXEogjNWPh2D0xk9YVKvrtGoHJNe9ebLu/pw= +github.com/uber/jaeger-lib v2.0.0+incompatible/go.mod h1:ComeNDZlWwrWnDv8aPp0Ba6+uUTzImX/AauajbLI56U= +go.etcd.io/bbolt v1.3.2 h1:Z/90sZLPOeCy2PwprqkFa25PdkusRzaj9P8zm/KNyvk= +go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= +golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= +golang.org/x/crypto v0.0.0-20181029021203-45a5f77698d3 h1:KYQXGkl6vs02hK7pK4eIbw0NpNPedieTSTEiJ//bwGs= +golang.org/x/crypto v0.0.0-20181029021203-45a5f77698d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= +golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= +golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20181023162649-9b4f9f5ad519/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190227022144-312bce6e941f h1:tbtX/qtlxzhZjgQue/7u7ygFwDEckd+DmS5+t8FgeKE= +golang.org/x/net v0.0.0-20190227022144-312bce6e941f/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= +golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4 h1:YUO/7uOKsKeq9UokNS62b8FYywz3ker1l1vDZRCRefw= +golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20181026203630-95b1ffbd15a5 h1:x6r4Jo0KNzOOzYd8lbcRsqjuqEASK6ob3auvWYM4/8U= +golang.org/x/sys v0.0.0-20181026203630-95b1ffbd15a5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +google.golang.org/appengine v1.1.0 h1:igQkv0AAhEIvTEpD5LIpAfav2eeVO9HBTjvKHVJPRSs= +google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= +google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= +google.golang.org/genproto v0.0.0-20180831171423-11092d34479b h1:lohp5blsw53GBXtLyLNaTXPXS9pJ1tiTw61ZHUoE9Qw= +google.golang.org/genproto v0.0.0-20180831171423-11092d34479b/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= +google.golang.org/grpc v1.19.0 h1:cfg4PD8YEdSFnm7qLV4++93WcmhH2nIUhMjhdCvl3j8= +google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/src-d/go-errors.v1 v1.0.0 h1:cooGdZnCjYbeS1zb1s6pVAAimTdKceRrpn7aKOnNIfc= +gopkg.in/src-d/go-errors.v1 v1.0.0/go.mod h1:q1cBlomlw2FnDBDNGlnh6X0jPihy+QxZfMMNxPCbdYg= +gopkg.in/src-d/go-vitess.v1 v1.5.0 h1:oOjzjcSli51TD44fXlJx4S/TZmE8kiNgZUgCuw5B4DU= +gopkg.in/src-d/go-vitess.v1 v1.5.0/go.mod h1:+g/wDtovsUgE2ioi32fo5Vavrtvfd/N1AvnknTmTvZE= +gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw= +gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= diff --git a/vendor/gopkg.in/src-d/go-mysql-server.v0/sql/expression/function/registry.go b/vendor/gopkg.in/src-d/go-mysql-server.v0/sql/expression/function/registry.go index 6c10e2646..4248e6874 100644 --- a/vendor/gopkg.in/src-d/go-mysql-server.v0/sql/expression/function/registry.go +++ b/vendor/gopkg.in/src-d/go-mysql-server.v0/sql/expression/function/registry.go @@ -8,65 +8,70 @@ import ( ) // Defaults is the function map with all the default functions. -var Defaults = sql.Functions{ - "count": sql.Function1(func(e sql.Expression) sql.Expression { - return aggregation.NewCount(e) - }), - "min": sql.Function1(func(e sql.Expression) sql.Expression { - return aggregation.NewMin(e) - }), - "max": sql.Function1(func(e sql.Expression) sql.Expression { - return aggregation.NewMax(e) - }), - "avg": sql.Function1(func(e sql.Expression) sql.Expression { - return aggregation.NewAvg(e) - }), - "sum": sql.Function1(func(e sql.Expression) sql.Expression { - return aggregation.NewSum(e) - }), - "is_binary": sql.Function1(NewIsBinary), - "substring": sql.FunctionN(NewSubstring), - "mid": sql.FunctionN(NewSubstring), - "substr": sql.FunctionN(NewSubstring), - "year": sql.Function1(NewYear), - "month": sql.Function1(NewMonth), - "day": sql.Function1(NewDay), - "weekday": sql.Function1(NewWeekday), - "hour": sql.Function1(NewHour), - "minute": sql.Function1(NewMinute), - "second": sql.Function1(NewSecond), - "dayofweek": sql.Function1(NewDayOfWeek), - "dayofyear": sql.Function1(NewDayOfYear), - "array_length": sql.Function1(NewArrayLength), - "split": sql.Function2(NewSplit), - "concat": sql.FunctionN(NewConcat), - "concat_ws": sql.FunctionN(NewConcatWithSeparator), - "coalesce": sql.FunctionN(NewCoalesce), - "lower": sql.Function1(NewLower), - "upper": sql.Function1(NewUpper), - "ceiling": sql.Function1(NewCeil), - "ceil": sql.Function1(NewCeil), - "floor": sql.Function1(NewFloor), - "round": sql.FunctionN(NewRound), - "connection_id": sql.Function0(NewConnectionID), - "soundex": sql.Function1(NewSoundex), - "json_extract": sql.FunctionN(NewJSONExtract), - "ln": sql.Function1(NewLogBaseFunc(float64(math.E))), - "log2": sql.Function1(NewLogBaseFunc(float64(2))), - "log10": sql.Function1(NewLogBaseFunc(float64(10))), - "log": sql.FunctionN(NewLog), - "rpad": sql.FunctionN(NewPadFunc(rPadType)), - "lpad": sql.FunctionN(NewPadFunc(lPadType)), - "sqrt": sql.Function1(NewSqrt), - "pow": sql.Function2(NewPower), - "power": sql.Function2(NewPower), - "ltrim": sql.Function1(NewTrimFunc(lTrimType)), - "rtrim": sql.Function1(NewTrimFunc(rTrimType)), - "trim": sql.Function1(NewTrimFunc(bTrimType)), - "reverse": sql.Function1(NewReverse), - "repeat": sql.Function2(NewRepeat), - "replace": sql.Function3(NewReplace), - "ifnull": sql.Function2(NewIfNull), - "nullif": sql.Function2(NewNullIf), - "now": sql.Function0(NewNow), +var Defaults = []sql.Function{ + sql.Function1{ + Name: "count", + Fn: func(e sql.Expression) sql.Expression { return aggregation.NewCount(e) }, + }, + sql.Function1{ + Name: "min", + Fn: func(e sql.Expression) sql.Expression { return aggregation.NewMin(e) }, + }, + sql.Function1{ + Name: "max", + Fn: func(e sql.Expression) sql.Expression { return aggregation.NewMax(e) }, + }, + sql.Function1{ + Name: "avg", + Fn: func(e sql.Expression) sql.Expression { return aggregation.NewAvg(e) }, + }, + sql.Function1{ + Name: "sum", + Fn: func(e sql.Expression) sql.Expression { return aggregation.NewSum(e) }, + }, + sql.Function1{Name: "is_binary", Fn: NewIsBinary}, + sql.FunctionN{Name: "substring", Fn: NewSubstring}, + sql.FunctionN{Name: "mid", Fn: NewSubstring}, + sql.FunctionN{Name: "substr", Fn: NewSubstring}, + sql.Function1{Name: "year", Fn: NewYear}, + sql.Function1{Name: "month", Fn: NewMonth}, + sql.Function1{Name: "day", Fn: NewDay}, + sql.Function1{Name: "weekday", Fn: NewWeekday}, + sql.Function1{Name: "hour", Fn: NewHour}, + sql.Function1{Name: "minute", Fn: NewMinute}, + sql.Function1{Name: "second", Fn: NewSecond}, + sql.Function1{Name: "dayofweek", Fn: NewDayOfWeek}, + sql.Function1{Name: "dayofyear", Fn: NewDayOfYear}, + sql.Function1{Name: "array_length", Fn: NewArrayLength}, + sql.Function2{Name: "split", Fn: NewSplit}, + sql.FunctionN{Name: "concat", Fn: NewConcat}, + sql.FunctionN{Name: "concat_ws", Fn: NewConcatWithSeparator}, + sql.FunctionN{Name: "coalesce", Fn: NewCoalesce}, + sql.Function1{Name: "lower", Fn: NewLower}, + sql.Function1{Name: "upper", Fn: NewUpper}, + sql.Function1{Name: "ceiling", Fn: NewCeil}, + sql.Function1{Name: "ceil", Fn: NewCeil}, + sql.Function1{Name: "floor", Fn: NewFloor}, + sql.FunctionN{Name: "round", Fn: NewRound}, + sql.Function0{Name: "connection_id", Fn: NewConnectionID}, + sql.Function1{Name: "soundex", Fn: NewSoundex}, + sql.FunctionN{Name: "json_extract", Fn: NewJSONExtract}, + sql.Function1{Name: "ln", Fn: NewLogBaseFunc(float64(math.E))}, + sql.Function1{Name: "log2", Fn: NewLogBaseFunc(float64(2))}, + sql.Function1{Name: "log10", Fn: NewLogBaseFunc(float64(10))}, + sql.FunctionN{Name: "log", Fn: NewLog}, + sql.FunctionN{Name: "rpad", Fn: NewPadFunc(rPadType)}, + sql.FunctionN{Name: "lpad", Fn: NewPadFunc(lPadType)}, + sql.Function1{Name: "sqrt", Fn: NewSqrt}, + sql.Function2{Name: "pow", Fn: NewPower}, + sql.Function2{Name: "power", Fn: NewPower}, + sql.Function1{Name: "ltrim", Fn: NewTrimFunc(lTrimType)}, + sql.Function1{Name: "rtrim", Fn: NewTrimFunc(rTrimType)}, + sql.Function1{Name: "trim", Fn: NewTrimFunc(bTrimType)}, + sql.Function1{Name: "reverse", Fn: NewReverse}, + sql.Function2{Name: "repeat", Fn: NewRepeat}, + sql.Function3{Name: "replace", Fn: NewReplace}, + sql.Function2{Name: "ifnull", Fn: NewIfNull}, + sql.Function2{Name: "nullif", Fn: NewNullIf}, + sql.Function0{Name: "now", Fn: NewNow}, } diff --git a/vendor/gopkg.in/src-d/go-mysql-server.v0/sql/functionregistry.go b/vendor/gopkg.in/src-d/go-mysql-server.v0/sql/functionregistry.go index 6a6c0ef3a..b78d7d373 100644 --- a/vendor/gopkg.in/src-d/go-mysql-server.v0/sql/functionregistry.go +++ b/vendor/gopkg.in/src-d/go-mysql-server.v0/sql/functionregistry.go @@ -4,122 +4,163 @@ import ( "gopkg.in/src-d/go-errors.v1" ) +// ErrFunctionAlreadyRegistered is thrown when a function is already registered +var ErrFunctionAlreadyRegistered = errors.NewKind("A function: '%s' is already registered.") + // ErrFunctionNotFound is thrown when a function is not found -var ErrFunctionNotFound = errors.NewKind("function not found: %s") +var ErrFunctionNotFound = errors.NewKind("A function: '%s' not found.") // ErrInvalidArgumentNumber is returned when the number of arguments to call a // function is different from the function arity. -var ErrInvalidArgumentNumber = errors.NewKind("%s: expecting %v arguments for calling this function, %d received") +var ErrInvalidArgumentNumber = errors.NewKind("A function: '%s' expected %d arguments, %d received.") -// Function is a function defined by the user that can be applied in a SQL -// query. +// Function is a function defined by the user that can be applied in a SQL query. type Function interface { // Call invokes the function. Call(...Expression) (Expression, error) + // Function name + name() string // isFunction will restrict implementations of Function isFunction() } type ( // Function0 is a function with 0 arguments. - Function0 func() Expression + Function0 struct { + Name string + Fn func() Expression + } // Function1 is a function with 1 argument. - Function1 func(e Expression) Expression + Function1 struct { + Name string + Fn func(e Expression) Expression + } // Function2 is a function with 2 arguments. - Function2 func(e1, e2 Expression) Expression + Function2 struct { + Name string + Fn func(e1, e2 Expression) Expression + } // Function3 is a function with 3 arguments. - Function3 func(e1, e2, e3 Expression) Expression + Function3 struct { + Name string + Fn func(e1, e2, e3 Expression) Expression + } // Function4 is a function with 4 arguments. - Function4 func(e1, e2, e3, e4 Expression) Expression + Function4 struct { + Name string + Fn func(e1, e2, e3, e4 Expression) Expression + } // Function5 is a function with 5 arguments. - Function5 func(e1, e2, e3, e4, e5 Expression) Expression + Function5 struct { + Name string + Fn func(e1, e2, e3, e4, e5 Expression) Expression + } // Function6 is a function with 6 arguments. - Function6 func(e1, e2, e3, e4, e5, e6 Expression) Expression + Function6 struct { + Name string + Fn func(e1, e2, e3, e4, e5, e6 Expression) Expression + } // Function7 is a function with 7 arguments. - Function7 func(e1, e2, e3, e4, e5, e6, e7 Expression) Expression + Function7 struct { + Name string + Fn func(e1, e2, e3, e4, e5, e6, e7 Expression) Expression + } // FunctionN is a function with variable number of arguments. This function // is expected to return ErrInvalidArgumentNumber if the arity does not // match, since the check has to be done in the implementation. - FunctionN func(...Expression) (Expression, error) + FunctionN struct { + Name string + Fn func(...Expression) (Expression, error) + } ) // Call implements the Function interface. func (fn Function0) Call(args ...Expression) (Expression, error) { if len(args) != 0 { - return nil, ErrInvalidArgumentNumber.New(0, len(args)) + return nil, ErrInvalidArgumentNumber.New(fn.Name, 0, len(args)) } - return fn(), nil + return fn.Fn(), nil } // Call implements the Function interface. func (fn Function1) Call(args ...Expression) (Expression, error) { if len(args) != 1 { - return nil, ErrInvalidArgumentNumber.New(1, len(args)) + return nil, ErrInvalidArgumentNumber.New(fn.Name, 1, len(args)) } - return fn(args[0]), nil + return fn.Fn(args[0]), nil } // Call implements the Function interface. func (fn Function2) Call(args ...Expression) (Expression, error) { if len(args) != 2 { - return nil, ErrInvalidArgumentNumber.New(2, len(args)) + return nil, ErrInvalidArgumentNumber.New(fn.Name, 2, len(args)) } - return fn(args[0], args[1]), nil + return fn.Fn(args[0], args[1]), nil } // Call implements the Function interface. func (fn Function3) Call(args ...Expression) (Expression, error) { if len(args) != 3 { - return nil, ErrInvalidArgumentNumber.New(3, len(args)) + return nil, ErrInvalidArgumentNumber.New(fn.Name, 3, len(args)) } - return fn(args[0], args[1], args[2]), nil + return fn.Fn(args[0], args[1], args[2]), nil } // Call implements the Function interface. func (fn Function4) Call(args ...Expression) (Expression, error) { if len(args) != 4 { - return nil, ErrInvalidArgumentNumber.New(4, len(args)) + return nil, ErrInvalidArgumentNumber.New(fn.Name, 4, len(args)) } - return fn(args[0], args[1], args[2], args[3]), nil + return fn.Fn(args[0], args[1], args[2], args[3]), nil } // Call implements the Function interface. func (fn Function5) Call(args ...Expression) (Expression, error) { if len(args) != 5 { - return nil, ErrInvalidArgumentNumber.New(5, len(args)) + return nil, ErrInvalidArgumentNumber.New(fn.Name, 5, len(args)) } - return fn(args[0], args[1], args[2], args[3], args[4]), nil + return fn.Fn(args[0], args[1], args[2], args[3], args[4]), nil } // Call implements the Function interface. func (fn Function6) Call(args ...Expression) (Expression, error) { if len(args) != 6 { - return nil, ErrInvalidArgumentNumber.New(6, len(args)) + return nil, ErrInvalidArgumentNumber.New(fn.Name, 6, len(args)) } - return fn(args[0], args[1], args[2], args[3], args[4], args[5]), nil + return fn.Fn(args[0], args[1], args[2], args[3], args[4], args[5]), nil } // Call implements the Function interface. func (fn Function7) Call(args ...Expression) (Expression, error) { if len(args) != 7 { - return nil, ErrInvalidArgumentNumber.New(7, len(args)) + return nil, ErrInvalidArgumentNumber.New(fn.Name, 7, len(args)) } - return fn(args[0], args[1], args[2], args[3], args[4], args[5], args[6]), nil + return fn.Fn(args[0], args[1], args[2], args[3], args[4], args[5], args[6]), nil } // Call implements the Function interface. func (fn FunctionN) Call(args ...Expression) (Expression, error) { - return fn(args...) + return fn.Fn(args...) } +func (fn Function0) name() string { return fn.Name } +func (fn Function1) name() string { return fn.Name } +func (fn Function2) name() string { return fn.Name } +func (fn Function3) name() string { return fn.Name } +func (fn Function4) name() string { return fn.Name } +func (fn Function5) name() string { return fn.Name } +func (fn Function6) name() string { return fn.Name } +func (fn Function7) name() string { return fn.Name } +func (fn FunctionN) name() string { return fn.Name } + func (Function0) isFunction() {} func (Function1) isFunction() {} func (Function2) isFunction() {} @@ -134,32 +175,37 @@ func (FunctionN) isFunction() {} // and User-Defined Functions. type FunctionRegistry map[string]Function -// Functions is a map of functions identified by their name. -type Functions map[string]Function - // NewFunctionRegistry creates a new FunctionRegistry. func NewFunctionRegistry() FunctionRegistry { return make(FunctionRegistry) } -// RegisterFunction registers a function with the given name. -func (r FunctionRegistry) RegisterFunction(name string, f Function) { - r[name] = f +// Register registers functions. +// If function with that name is already registered, +// the ErrFunctionAlreadyRegistered will be returned +func (r FunctionRegistry) Register(fn ...Function) error { + for _, f := range fn { + if _, ok := r[f.name()]; ok { + return ErrFunctionAlreadyRegistered.New(f.name()) + } + r[f.name()] = f + } + return nil } -// RegisterFunctions registers a map of functions. -func (r FunctionRegistry) RegisterFunctions(funcs Functions) { - for name, f := range funcs { - r[name] = f +// MustRegister registers functions. +// If function with that name is already registered, it will panic! +func (r FunctionRegistry) MustRegister(fn ...Function) { + if err := r.Register(fn...); err != nil { + panic(err) } } // Function returns a function with the given name. func (r FunctionRegistry) Function(name string) (Function, error) { - e, ok := r[name] - if !ok { - return nil, ErrFunctionNotFound.New(name) + if fn, ok := r[name]; ok { + return fn, nil } - return e, nil + return nil, ErrFunctionNotFound.New(name) } diff --git a/vendor/gopkg.in/src-d/go-mysql-server.v0/sql/index/pilosa/driver.go b/vendor/gopkg.in/src-d/go-mysql-server.v0/sql/index/pilosa/driver.go index 180e29fa3..519003efc 100644 --- a/vendor/gopkg.in/src-d/go-mysql-server.v0/sql/index/pilosa/driver.go +++ b/vendor/gopkg.in/src-d/go-mysql-server.v0/sql/index/pilosa/driver.go @@ -8,7 +8,11 @@ import ( "io/ioutil" "os" "path/filepath" + "runtime" + "strconv" "strings" + "sync" + "sync/atomic" "time" opentracing "github.com/opentracing/opentracing-go" @@ -49,6 +53,11 @@ var ( errInvalidIndexType = errors.NewKind("expecting a pilosa index, instead got %T") ) +const ( + pilosaIndexThreadsKey = "PILOSA_INDEX_THREADS" + pilosaIndexThreadsVar = "pilosa_index_threads" +) + type ( bitBatch struct { size uint64 @@ -217,13 +226,13 @@ func (d *Driver) savePartition( kviter sql.IndexKeyValueIter, idx *pilosaIndex, pilosaIndex *concurrentPilosaIndex, - offset uint64, b *batch, ) (uint64, error) { var ( colID uint64 err error ) + for i, e := range idx.Expressions() { name := fieldName(idx.ID(), e, p) pilosaIndex.DeleteField(name) @@ -254,7 +263,7 @@ func (d *Driver) savePartition( kviter.Close() }() - for colID = offset; err == nil; colID++ { + for colID = 0; err == nil; colID++ { // commit each batch of objects (pilosa and boltdb) if colID%sql.IndexBatchSize == 0 && colID != 0 { if err = d.saveBatch(ctx, idx.mapping, colID, b); err != nil { @@ -265,28 +274,30 @@ func (d *Driver) savePartition( select { case <-ctx.Context.Done(): return 0, ctx.Context.Err() - default: - var ( - values []interface{} - location []byte - ) - if values, location, err = kviter.Next(); err != nil { - break - } + } - for i, field := range b.fields { - if values[i] == nil { - continue - } + values, location, err := kviter.Next() + if err != nil { + break + } - rowID, err := idx.mapping.getRowID(field.Name(), values[i]) - if err != nil { - return 0, err - } - b.bitBatches[i].Add(rowID, colID) + for i, field := range b.fields { + if values[i] == nil { + continue + } + + rowID, err := idx.mapping.getRowID(field.Name(), values[i]) + if err != nil { + return 0, err } - err = idx.mapping.putLocation(pilosaIndex.Name(), colID, location) + + b.bitBatches[i].Add(rowID, colID) + } + + err = idx.mapping.putLocation(pilosaIndex.Name(), p, colID, location) + if err != nil { + return 0, err } } @@ -307,7 +318,7 @@ func (d *Driver) savePartition( } } - return colID - offset, err + return colID, err } // Save the given index (mapping and bitmap) @@ -331,44 +342,86 @@ func (d *Driver) Save( idx.wg.Add(1) defer idx.wg.Done() - var b = batch{ - fields: make([]*pilosa.Field, len(idx.Expressions())), - bitBatches: make([]*bitBatch, len(idx.Expressions())), - } - ctx.Context, idx.cancel = context.WithCancel(ctx.Context) processingFile := d.processingFilePath(i.Database(), i.Table(), i.ID()) - if err := index.WriteProcessingFile( + err = index.WriteProcessingFile( processingFile, []byte{processingFileOnSave}, - ); err != nil { + ) + if err != nil { return err } defer iter.Close() pilosaIndex := idx.index - var rows uint64 + + var ( + rows, timePilosa, timeMapping uint64 + + wg sync.WaitGroup + tokens = make(chan struct{}, indexThreads(ctx)) + + errors []error + errmut sync.Mutex + ) + for { + select { + case <-ctx.Done(): + return + default: + } + p, kviter, err := iter.Next() if err != nil { if err == io.EOF { break } - return err - } - numRows, err := d.savePartition(ctx, p, kviter, idx, pilosaIndex, rows, &b) - if err != nil { + idx.cancel() + wg.Wait() return err } - rows += numRows + wg.Add(1) + + go func() { + defer func() { + wg.Done() + <-tokens + }() + + tokens <- struct{}{} + + var b = &batch{ + fields: make([]*pilosa.Field, len(idx.Expressions())), + bitBatches: make([]*bitBatch, len(idx.Expressions())), + } + + numRows, err := d.savePartition(ctx, p, kviter, idx, pilosaIndex, b) + if err != nil { + errmut.Lock() + errors = append(errors, err) + idx.cancel() + errmut.Unlock() + return + } + + atomic.AddUint64(&timeMapping, uint64(b.timeMapping)) + atomic.AddUint64(&timePilosa, uint64(b.timePilosa)) + atomic.AddUint64(&rows, numRows) + }() + } + + wg.Wait() + if len(errors) > 0 { + return errors[0] } logrus.WithFields(logrus.Fields{ "duration": time.Since(start), - "pilosa": b.timePilosa, - "mapping": b.timeMapping, + "pilosa": timePilosa, + "mapping": timeMapping, "rows": rows, "id": i.ID(), }).Debugf("finished pilosa indexing") @@ -421,18 +474,18 @@ func (d *Driver) Delete(i sql.Index, partitions sql.PartitionIter) error { return partitions.Close() } -func (d *Driver) saveBatch(ctx *sql.Context, m *mapping, colID uint64, b *batch) error { - err := d.savePilosa(ctx, colID, b) +func (d *Driver) saveBatch(ctx *sql.Context, m *mapping, cols uint64, b *batch) error { + err := d.savePilosa(ctx, cols, b) if err != nil { return err } - return d.saveMapping(ctx, m, colID, true, b) + return d.saveMapping(ctx, m, cols, true, b) } -func (d *Driver) savePilosa(ctx *sql.Context, colID uint64, b *batch) error { +func (d *Driver) savePilosa(ctx *sql.Context, cols uint64, b *batch) error { span, _ := ctx.Span("pilosa.Save.bitBatch", - opentracing.Tag{Key: "cols", Value: colID}, + opentracing.Tag{Key: "cols", Value: cols}, opentracing.Tag{Key: "fields", Value: len(b.fields)}, ) defer span.Finish() @@ -457,12 +510,12 @@ func (d *Driver) savePilosa(ctx *sql.Context, colID uint64, b *batch) error { func (d *Driver) saveMapping( ctx *sql.Context, m *mapping, - colID uint64, + cols uint64, cont bool, b *batch, ) error { span, _ := ctx.Span("pilosa.Save.mapping", - opentracing.Tag{Key: "cols", Value: colID}, + opentracing.Tag{Key: "cols", Value: cols}, opentracing.Tag{Key: "continues", Value: cont}, ) defer span.Finish() @@ -541,3 +594,21 @@ func (d *Driver) newPilosaIndex(db, table string) (*pilosa.Index, error) { } return idx, nil } + +func indexThreads(ctx *sql.Context) int { + typ, val := ctx.Session.Get(pilosaIndexThreadsVar) + if val != nil && typ == sql.Int64 { + return int(val.(int64)) + } + + var value int + if v, ok := os.LookupEnv(pilosaIndexThreadsKey); ok { + value, _ = strconv.Atoi(v) + } + + if value <= 0 { + value = runtime.NumCPU() + } + + return value +} diff --git a/vendor/gopkg.in/src-d/go-mysql-server.v0/sql/index/pilosa/iterator.go b/vendor/gopkg.in/src-d/go-mysql-server.v0/sql/index/pilosa/iterator.go index 747916049..138ac9156 100644 --- a/vendor/gopkg.in/src-d/go-mysql-server.v0/sql/index/pilosa/iterator.go +++ b/vendor/gopkg.in/src-d/go-mysql-server.v0/sql/index/pilosa/iterator.go @@ -5,6 +5,7 @@ import ( "github.com/sirupsen/logrus" bolt "go.etcd.io/bbolt" + "gopkg.in/src-d/go-mysql-server.v0/sql" ) type locationValueIter struct { @@ -31,6 +32,7 @@ type indexValueIter struct { total uint64 bits []uint64 mapping *mapping + partition sql.Partition indexName string // share transaction and bucket on all getLocation calls @@ -45,7 +47,7 @@ func (it *indexValueIter) Next() ([]byte, error) { return nil, err } - bucket, err := it.mapping.getBucket(it.indexName, false) + bucket, err := it.mapping.getBucket(it.indexName, it.partition, false) if err != nil { _ = it.Close() return nil, err diff --git a/vendor/gopkg.in/src-d/go-mysql-server.v0/sql/index/pilosa/lookup.go b/vendor/gopkg.in/src-d/go-mysql-server.v0/sql/index/pilosa/lookup.go index 225c464a0..d5fc47505 100644 --- a/vendor/gopkg.in/src-d/go-mysql-server.v0/sql/index/pilosa/lookup.go +++ b/vendor/gopkg.in/src-d/go-mysql-server.v0/sql/index/pilosa/lookup.go @@ -154,7 +154,11 @@ func (l *indexLookup) Values(p sql.Partition) (sql.IndexValueIter, error) { } if row == nil { - return &indexValueIter{mapping: l.mapping, indexName: l.index.Name()}, nil + return &indexValueIter{ + mapping: l.mapping, + indexName: l.index.Name(), + partition: p, + }, nil } bits := row.Columns() @@ -163,6 +167,7 @@ func (l *indexLookup) Values(p sql.Partition) (sql.IndexValueIter, error) { bits: bits, mapping: l.mapping, indexName: l.index.Name(), + partition: p, }, nil } @@ -315,15 +320,20 @@ func (l *filteredLookup) Values(p sql.Partition) (sql.IndexValueIter, error) { } if row == nil { - return &indexValueIter{mapping: l.mapping, indexName: l.index.Name()}, nil + return &indexValueIter{ + mapping: l.mapping, + indexName: l.index.Name(), + partition: p, + }, nil } bits := row.Columns() if err := l.mapping.open(); err != nil { return nil, err } + defer l.mapping.close() - locations, err := l.mapping.sortedLocations(l.index.Name(), bits, l.reverse) + locations, err := l.mapping.sortedLocations(l.index.Name(), p, bits, l.reverse) if err != nil { return nil, err } @@ -500,7 +510,11 @@ func (l *negateLookup) Values(p sql.Partition) (sql.IndexValueIter, error) { } if row == nil { - return &indexValueIter{mapping: l.mapping, indexName: l.index.Name()}, nil + return &indexValueIter{ + mapping: l.mapping, + indexName: l.index.Name(), + partition: p, + }, nil } bits := row.Columns() @@ -509,6 +523,7 @@ func (l *negateLookup) Values(p sql.Partition) (sql.IndexValueIter, error) { bits: bits, mapping: l.mapping, indexName: l.index.Name(), + partition: p, }, nil } diff --git a/vendor/gopkg.in/src-d/go-mysql-server.v0/sql/index/pilosa/mapping.go b/vendor/gopkg.in/src-d/go-mysql-server.v0/sql/index/pilosa/mapping.go index e77cee62c..4642b96de 100644 --- a/vendor/gopkg.in/src-d/go-mysql-server.v0/sql/index/pilosa/mapping.go +++ b/vendor/gopkg.in/src-d/go-mysql-server.v0/sql/index/pilosa/mapping.go @@ -10,6 +10,7 @@ import ( "sync" bolt "go.etcd.io/bbolt" + "gopkg.in/src-d/go-mysql-server.v0/sql" ) // mapping @@ -127,20 +128,19 @@ func (m *mapping) rollback() error { } func (m *mapping) transaction(writable bool, f func(*bolt.Tx) error) error { + m.clientMut.Lock() + defer m.clientMut.Unlock() + var tx *bolt.Tx var err error if m.create { - m.clientMut.Lock() if m.tx == nil { m.tx, err = m.db.Begin(true) if err != nil { - m.clientMut.Unlock() return err } } - m.clientMut.Unlock() - tx = m.tx } else { tx, err = m.db.Begin(writable) @@ -150,7 +150,6 @@ func (m *mapping) transaction(writable bool, f func(*bolt.Tx) error) error { } err = f(tx) - if m.create { return err } @@ -217,9 +216,14 @@ func (m *mapping) getMaxRowID(fieldName string) (uint64, error) { return id, err } -func (m *mapping) putLocation(indexName string, colID uint64, location []byte) error { +func (m *mapping) putLocation( + indexName string, + partition sql.Partition, + colID uint64, + location []byte, +) error { return m.transaction(true, func(tx *bolt.Tx) error { - b, err := tx.CreateBucketIfNotExists([]byte(indexName)) + b, err := tx.CreateBucketIfNotExists(indexPartitionKey(indexName, partition)) if err != nil { return err } @@ -231,14 +235,24 @@ func (m *mapping) putLocation(indexName string, colID uint64, location []byte) e }) } -func (m *mapping) sortedLocations(indexName string, cols []uint64, reverse bool) ([][]byte, error) { +func indexPartitionKey(indexName string, partition sql.Partition) []byte { + return []byte(indexName + string(partition.Key())) +} + +func (m *mapping) sortedLocations( + indexName string, + partition sql.Partition, + cols []uint64, + reverse bool, +) ([][]byte, error) { var result [][]byte m.mut.RLock() defer m.mut.RUnlock() err := m.db.View(func(tx *bolt.Tx) error { - b := tx.Bucket([]byte(indexName)) + bucket := indexPartitionKey(indexName, partition) + b := tx.Bucket(bucket) if b == nil { - return fmt.Errorf("bucket %s not found", indexName) + return fmt.Errorf("bucket %s not found", bucket) } for _, col := range cols { @@ -274,13 +288,18 @@ func (b byBytes) Len() int { return len(b) } func (b byBytes) Swap(i, j int) { b[i], b[j] = b[j], b[i] } func (b byBytes) Less(i, j int) bool { return bytes.Compare(b[i], b[j]) < 0 } -func (m *mapping) getLocation(indexName string, colID uint64) ([]byte, error) { +func (m *mapping) getLocation( + indexName string, + partition sql.Partition, + colID uint64, +) ([]byte, error) { var location []byte err := m.transaction(true, func(tx *bolt.Tx) error { - b := tx.Bucket([]byte(indexName)) + bucket := indexPartitionKey(indexName, partition) + b := tx.Bucket(bucket) if b == nil { - return fmt.Errorf("bucket %s not found", indexName) + return fmt.Errorf("bucket %s not found", bucket) } key := make([]byte, 8) @@ -304,6 +323,7 @@ func (m *mapping) getLocationFromBucket( func (m *mapping) getBucket( indexName string, + partition sql.Partition, writable bool, ) (*bolt.Bucket, error) { var bucket *bolt.Bucket @@ -313,10 +333,11 @@ func (m *mapping) getBucket( return nil, err } - bucket = tx.Bucket([]byte(indexName)) + bu := indexPartitionKey(indexName, partition) + bucket = tx.Bucket(bu) if bucket == nil { _ = tx.Rollback() - return nil, fmt.Errorf("bucket %s not found", indexName) + return nil, fmt.Errorf("bucket %s not found", bu) } return bucket, err diff --git a/vendor/gopkg.in/src-d/go-mysql-server.v0/sql/parse/show_create.go b/vendor/gopkg.in/src-d/go-mysql-server.v0/sql/parse/show_create.go index 68ea2ee7b..2ef5989b5 100644 --- a/vendor/gopkg.in/src-d/go-mysql-server.v0/sql/parse/show_create.go +++ b/vendor/gopkg.in/src-d/go-mysql-server.v0/sql/parse/show_create.go @@ -2,10 +2,9 @@ package parse import ( "bufio" + "io" "strings" -) -import ( "gopkg.in/src-d/go-errors.v1" "gopkg.in/src-d/go-mysql-server.v0/sql" "gopkg.in/src-d/go-mysql-server.v0/sql/plan" @@ -31,10 +30,24 @@ func parseShowCreate(s string) (sql.Node, error) { switch strings.ToLower(thingToShow) { case "table": - var name string + var db, table string + + if err := readQuotableIdent(&table)(r); err != nil { + return nil, err + } + + ru, _, err := r.ReadRune() + if err != nil && err != io.EOF { + return nil, err + } else if err == nil && ru == '.' { + db = table + + if err := readQuotableIdent(&table)(r); err != nil { + return nil, err + } + } - err := parseFuncs{ - readQuotableIdent(&name), + err = parseFuncs{ skipSpaces, checkEOF, }.exec(r) @@ -43,9 +56,9 @@ func parseShowCreate(s string) (sql.Node, error) { } return plan.NewShowCreateTable( - sql.UnresolvedDatabase("").Name(), + db, nil, - name), nil + table), nil case "database", "schema": var ifNotExists bool var next string diff --git a/vendor/gopkg.in/src-d/go-mysql-server.v0/sql/plan/innerjoin.go b/vendor/gopkg.in/src-d/go-mysql-server.v0/sql/plan/innerjoin.go index 86bdcd1ac..aa55c2a35 100644 --- a/vendor/gopkg.in/src-d/go-mysql-server.v0/sql/plan/innerjoin.go +++ b/vendor/gopkg.in/src-d/go-mysql-server.v0/sql/plan/innerjoin.go @@ -15,10 +15,10 @@ import ( ) const ( - experimentalInMemoryJoinKey = "EXPERIMENTAL_IN_MEMORY_JOIN" - maxMemoryJoinKey = "MAX_MEMORY_INNER_JOIN" - inMemoryJoinSessionVar = "inmemory_joins" - memoryThresholdSessionVar = "max_memory_joins" + inMemoryJoinKey = "INMEMORY_JOINS" + maxMemoryJoinKey = "MAX_MEMORY_INNER_JOIN" + inMemoryJoinSessionVar = "inmemory_joins" + memoryThresholdSessionVar = "max_memory_joins" ) var ( @@ -32,7 +32,7 @@ var ( ) func shouldUseMemoryJoinsByEnv() bool { - v := strings.TrimSpace(strings.ToLower(os.Getenv(experimentalInMemoryJoinKey))) + v := strings.TrimSpace(strings.ToLower(os.Getenv(inMemoryJoinKey))) return v == "on" || v == "1" } @@ -48,7 +48,7 @@ func loadMemoryThreshold() uint64 { return defaultMemoryThreshold } - return n + return n * 1024 // to bytes } // InnerJoin is an inner join between two tables. @@ -252,7 +252,7 @@ func (i *innerJoinIter) fitsInMemory() bool { var maxMemory uint64 _, v := i.ctx.Session.Get(memoryThresholdSessionVar) if n, ok := v.(int64); ok { - maxMemory = uint64(n) + maxMemory = uint64(n) * 1024 // to bytes } else { maxMemory = maxMemoryJoin } diff --git a/vendor/gopkg.in/src-d/go-mysql-server.v0/sql/plan/show_create_table.go b/vendor/gopkg.in/src-d/go-mysql-server.v0/sql/plan/show_create_table.go index 853613e62..300725af1 100644 --- a/vendor/gopkg.in/src-d/go-mysql-server.v0/sql/plan/show_create_table.go +++ b/vendor/gopkg.in/src-d/go-mysql-server.v0/sql/plan/show_create_table.go @@ -78,7 +78,7 @@ func (i *showCreateTablesIter) Next() (sql.Row, error) { composedCreateTableStatement := produceCreateStatement(table) return sql.NewRow( - i.table, // "Table" string + i.table, // "Table" string composedCreateTableStatement, // "Create Table" string ), nil } @@ -89,7 +89,8 @@ func produceCreateStatement(table sql.Table) string { // Statement creation parts for each column for indx, col := range schema { - createStmtPart := fmt.Sprintf(" `%s` %s", col.Name, col.Type.Type()) + createStmtPart := fmt.Sprintf(" `%s` %s", col.Name, + strings.ToLower(col.Type.Type().String())) if !col.Nullable { createStmtPart = fmt.Sprintf("%s NOT NULL", createStmtPart) From 9cc39b94d1c87a43f0b266ec62716d92c277c31f Mon Sep 17 00:00:00 2001 From: Miguel Molina Date: Thu, 11 Apr 2019 11:22:31 +0200 Subject: [PATCH 3/4] fixes after upgrade Signed-off-by: Miguel Molina --- cmd/gitbase/command/server.go | 2 +- integration_test.go | 6 +----- internal/function/registry.go | 18 +++++++++--------- 3 files changed, 11 insertions(+), 15 deletions(-) diff --git a/cmd/gitbase/command/server.go b/cmd/gitbase/command/server.go index e49f6b810..f93d763ce 100644 --- a/cmd/gitbase/command/server.go +++ b/cmd/gitbase/command/server.go @@ -219,7 +219,7 @@ func (c *Server) buildDatabase() error { c.engine.Catalog.SetCurrentDatabase(c.Name) logrus.WithField("db", c.Name).Debug("registered database to catalog") - c.engine.Catalog.RegisterFunctions(function.Functions) + c.engine.Catalog.MustRegister(function.Functions...) logrus.Debug("registered all available functions in catalog") if err := c.registerDrivers(); err != nil { diff --git a/integration_test.go b/integration_test.go index 2cc717252..1c05861d3 100644 --- a/integration_test.go +++ b/integration_test.go @@ -21,7 +21,6 @@ import ( "gopkg.in/src-d/go-mysql-server.v0/auth" "gopkg.in/src-d/go-mysql-server.v0/sql" "gopkg.in/src-d/go-mysql-server.v0/sql/analyzer" - sqlfunction "gopkg.in/src-d/go-mysql-server.v0/sql/expression/function" "gopkg.in/src-d/go-mysql-server.v0/sql/index/pilosa" ) @@ -956,12 +955,9 @@ func setup(t testing.TB) (*sqle.Engine, *gitbase.RepositoryPool, func()) { func newSquashEngine(pool *gitbase.RepositoryPool) *sqle.Engine { engine := newBaseEngine(pool) - - engine.Catalog.RegisterFunctions(sqlfunction.Defaults) engine.Analyzer = analyzer.NewBuilder(engine.Catalog). AddPostAnalyzeRule(rule.SquashJoinsRule, rule.SquashJoins). Build() - return engine } @@ -971,6 +967,6 @@ func newBaseEngine(pool *gitbase.RepositoryPool) *sqle.Engine { engine := command.NewDatabaseEngine(au, "test", 0, false) engine.AddDatabase(foo) - engine.Catalog.RegisterFunctions(function.Functions) + engine.Catalog.MustRegister(function.Functions...) return engine } diff --git a/internal/function/registry.go b/internal/function/registry.go index 94b86f189..53a8ab45f 100644 --- a/internal/function/registry.go +++ b/internal/function/registry.go @@ -3,13 +3,13 @@ package function import "gopkg.in/src-d/go-mysql-server.v0/sql" // Functions for gitbase queries. -var Functions = sql.Functions{ - "is_tag": sql.Function1(NewIsTag), - "is_remote": sql.Function1(NewIsRemote), - "language": sql.FunctionN(NewLanguage), - "uast": sql.FunctionN(NewUAST), - "uast_mode": sql.Function3(NewUASTMode), - "uast_xpath": sql.Function2(NewUASTXPath), - "uast_extract": sql.Function2(NewUASTExtract), - "uast_children": sql.Function1(NewUASTChildren), +var Functions = []sql.Function{ + sql.Function1{Name: "is_tag", Fn: NewIsTag}, + sql.Function1{Name: "is_remote", Fn: NewIsRemote}, + sql.FunctionN{Name: "language", Fn: NewLanguage}, + sql.FunctionN{Name: "uast", Fn: NewUAST}, + sql.Function3{Name: "uast_mode", Fn: NewUASTMode}, + sql.Function2{Name: "uast_xpath", Fn: NewUASTXPath}, + sql.Function2{Name: "uast_extract", Fn: NewUASTExtract}, + sql.Function1{Name: "uast_children", Fn: NewUASTChildren}, } From 9675cda83a847a09e9d79d6cd5429d78b4b06011 Mon Sep 17 00:00:00 2001 From: Miguel Molina Date: Fri, 12 Apr 2019 13:35:19 +0200 Subject: [PATCH 4/4] vendor: upgrade go-mysql-server Signed-off-by: Miguel Molina --- Gopkg.lock | 5 +- Gopkg.toml | 2 +- docs/using-gitbase/functions.md | 10 +- docs/using-gitbase/indexes.md | 2 +- docs/using-gitbase/supported-syntax.md | 13 +- .../src-d/go-mysql-server.v0/README.md | 10 +- .../src-d/go-mysql-server.v0/SUPPORTED.md | 13 +- .../go-mysql-server.v0/server/handler.go | 6 +- .../sql/analyzer/validation_rules.go | 38 +++ .../sql/expression/arithmetic.go | 88 ++++-- .../sql/expression/function/date.go | 177 +++++++++++ .../sql/expression/function/registry.go | 8 + .../sql/expression/function/sleep.go | 66 ++++ .../sql/expression/function/time.go | 254 +++++++++++++++- .../function/tobase64_frombase64.go | 148 +++++++++ .../sql/expression/interval.go | 286 ++++++++++++++++++ .../go-mysql-server.v0/sql/parse/parse.go | 21 ++ 17 files changed, 1107 insertions(+), 40 deletions(-) create mode 100644 vendor/gopkg.in/src-d/go-mysql-server.v0/sql/expression/function/date.go create mode 100644 vendor/gopkg.in/src-d/go-mysql-server.v0/sql/expression/function/sleep.go create mode 100644 vendor/gopkg.in/src-d/go-mysql-server.v0/sql/expression/function/tobase64_frombase64.go create mode 100644 vendor/gopkg.in/src-d/go-mysql-server.v0/sql/expression/interval.go diff --git a/Gopkg.lock b/Gopkg.lock index b35aa2a7f..0c665ff75 100644 --- a/Gopkg.lock +++ b/Gopkg.lock @@ -672,7 +672,7 @@ source = "github.com/src-d/go-git" [[projects]] - digest = "1:3b3fa49a96ab0ff715b28b6baf0ae8a224b35dc01eaa6dd3358071dab0c01930" + digest = "1:dd08673c079d096ae0cd2169b8edf2d35866bb1a7b96a1d95cdd85d48873bbb7" name = "gopkg.in/src-d/go-mysql-server.v0" packages = [ ".", @@ -691,7 +691,7 @@ "sql/plan", ] pruneopts = "UT" - revision = "d03de5f7c0d7b9e9920c7654efc61eabe988dabe" + revision = "bb5fe96ff756e5b25ec53c68c92d92c108832a65" [[projects]] digest = "1:f995136b53497081f1b3f29b99e78a597da6afb2bc6f22908382559a863df4ea" @@ -801,7 +801,6 @@ "gopkg.in/src-d/go-mysql-server.v0/sql", "gopkg.in/src-d/go-mysql-server.v0/sql/analyzer", "gopkg.in/src-d/go-mysql-server.v0/sql/expression", - "gopkg.in/src-d/go-mysql-server.v0/sql/expression/function", "gopkg.in/src-d/go-mysql-server.v0/sql/index/pilosa", "gopkg.in/src-d/go-mysql-server.v0/sql/parse", "gopkg.in/src-d/go-mysql-server.v0/sql/plan", diff --git a/Gopkg.toml b/Gopkg.toml index ce7b43c0d..3276c7a70 100644 --- a/Gopkg.toml +++ b/Gopkg.toml @@ -1,6 +1,6 @@ [[constraint]] name = "gopkg.in/src-d/go-mysql-server.v0" - revision = "d03de5f7c0d7b9e9920c7654efc61eabe988dabe" + revision = "bb5fe96ff756e5b25ec53c68c92d92c108832a65" [[constraint]] name = "github.com/jessevdk/go-flags" diff --git a/docs/using-gitbase/functions.md b/docs/using-gitbase/functions.md index ae86f143e..47b196e96 100644 --- a/docs/using-gitbase/functions.md +++ b/docs/using-gitbase/functions.md @@ -31,7 +31,11 @@ These are all functions that are available because they are implemented in `go-m |`CONCAT_WS(sep, ...)`|Concatenate any group of fields into a single string. The first argument is the separator for the rest of the arguments. The separator is added between the strings to be concatenated. The separator can be a string, as can the rest of the arguments. If the separator is NULL, the result is NULL.| |`CONNECTION_ID()`|Return the current connection ID.| |`COUNT(expr)`| Returns a count of the number of non-NULL values of expr in the rows retrieved by a SELECT statement.| -|`DAY(date)`|Returns the day of the given date.| +|`DATE_ADD(date, interval)`|Adds the interval to the given date.| +|`DATE_SUB(date, interval)`|Subtracts the interval from the given date.| +|`DAY(date)`|Synonym for DAYOFMONTH().| +|`DATE(date)`|Returns the date part of the given date.| +|`DAYOFMONTH(date)`|Return the day of the month (0-31).| |`DAYOFWEEK(date)`|Returns the day of the week of the given date.| |`DAYOFYEAR(date)`|Returns the day of the year of the given date.| |`FLOOR(number)`|Return the largest integer value that is less than or equal to `number`.| @@ -61,16 +65,20 @@ These are all functions that are available because they are implemented in `go-m |`RPAD(str, len, padstr)`|Returns the string str, right-padded with the string padstr to a length of len characters.| |`RTRIM(str)`|Returns the string str with trailing space characters removed.| |`SECOND(date)`|Returns the seconds of the given date.| +|`SLEEP(seconds)`|Wait for the specified number of seconds (can be fractional).| |`SOUNDEX(str)`|Returns the soundex of a string.| |`SPLIT(str,sep)`|Receives a string and a separator and returns the parts of the string split by the separator as a JSON array of strings.| |`SQRT(X)`|Returns the square root of a nonnegative number X.| |`SUBSTR(str, pos, [len])`|Return a substring from the provided string starting at `pos` with a length of `len` characters. If no `len` is provided, all characters from `pos` until the end will be taken.| |`SUBSTRING(str, pos, [len])`|Return a substring from the provided string starting at `pos` with a length of `len` characters. If no `len` is provided, all characters from `pos` until the end will be taken.| |`SUM(expr)`|Returns the sum of expr in all rows.| +|`TO_BASE64(str)`|Encodes the string str in base64 format.| +|`FROM_BASE64(str)`|Decodes the base64-encoded string str.| |`TRIM(str)`|Returns the string str with all spaces removed.| |`UPPER(str)`|Returns the string str with all characters in upper case.| |`WEEKDAY(date)`|Returns the weekday of the given date.| |`YEAR(date)`|Returns the year of the given date.| +|`YEARWEEK(date, mode)`|Returns year and week for a date. The year in the result may be different from the year in the date argument for the first and the last week of the year.| ## Note about uast, uast_mode, uast_xpath and uast_children functions diff --git a/docs/using-gitbase/indexes.md b/docs/using-gitbase/indexes.md index 74e676e7f..77678f10d 100644 --- a/docs/using-gitbase/indexes.md +++ b/docs/using-gitbase/indexes.md @@ -26,4 +26,4 @@ and for the second query also two indexes will be used and the result will be a You can find some more examples in the [examples](./examples.md#create-an-index-for-columns-on-a-table) section. -See [go-mysql-server](https://github.com/src-d/go-mysql-server/tree/d03de5f7c0d7b9e9920c7654efc61eabe988dabe#indexes) documentation for more details +See [go-mysql-server](https://github.com/src-d/go-mysql-server/tree/bb5fe96ff756e5b25ec53c68c92d92c108832a65#indexes) documentation for more details diff --git a/docs/using-gitbase/supported-syntax.md b/docs/using-gitbase/supported-syntax.md index 84d33a0e0..a6f57ad5a 100644 --- a/docs/using-gitbase/supported-syntax.md +++ b/docs/using-gitbase/supported-syntax.md @@ -50,6 +50,7 @@ - USE - SHOW DATABASES - SHOW WARNINGS +- INTERVALS ## Index expressions - CREATE INDEX (an index can be created using either column names or a single arbitrary expression). @@ -67,8 +68,8 @@ - OR ## Arithmetic expressions -- \+ -- \- +- \+ (including between dates and intervals) +- \- (including between dates and intervals) - \* - \\ - << @@ -109,10 +110,15 @@ - LN - LOG2 - LOG10 +- SLEEP +- TO_BASE64 +- FROM_BASE64 ## Time functions +- DATE - DAY - WEEKDAY +- DAYOFMONTH - DAYOFWEEK - DAYOFYEAR - HOUR @@ -120,4 +126,7 @@ - MONTH - SECOND - YEAR +- YEARWEEK - NOW +- DATE_ADD +- DATE_SUB diff --git a/vendor/gopkg.in/src-d/go-mysql-server.v0/README.md b/vendor/gopkg.in/src-d/go-mysql-server.v0/README.md index bc09e3f53..805a07e9e 100644 --- a/vendor/gopkg.in/src-d/go-mysql-server.v0/README.md +++ b/vendor/gopkg.in/src-d/go-mysql-server.v0/README.md @@ -68,7 +68,11 @@ We support and actively test against certain third-party clients to ensure compa |`CONCAT_WS(sep, ...)`|Concatenate any group of fields into a single string. The first argument is the separator for the rest of the arguments. The separator is added between the strings to be concatenated. The separator can be a string, as can the rest of the arguments. If the separator is NULL, the result is NULL.| |`CONNECTION_ID()`|Return the current connection ID.| |`COUNT(expr)`| Returns a count of the number of non-NULL values of expr in the rows retrieved by a SELECT statement.| -|`DAY(date)`|Returns the day of the given date.| +|`DATE_ADD(date, interval)`|Adds the interval to the given date.| +|`DATE_SUB(date, interval)`|Subtracts the interval from the given date.| +|`DAY(date)`|Synonym for DAYOFMONTH().| +|`DATE(date)`|Returns the date part of the given date.| +|`DAYOFMONTH(date)`|Return the day of the month (0-31).| |`DAYOFWEEK(date)`|Returns the day of the week of the given date.| |`DAYOFYEAR(date)`|Returns the day of the year of the given date.| |`FLOOR(number)`|Return the largest integer value that is less than or equal to `number`.| @@ -98,16 +102,20 @@ We support and actively test against certain third-party clients to ensure compa |`RPAD(str, len, padstr)`|Returns the string str, right-padded with the string padstr to a length of len characters.| |`RTRIM(str)`|Returns the string str with trailing space characters removed.| |`SECOND(date)`|Returns the seconds of the given date.| +|`SLEEP(seconds)`|Wait for the specified number of seconds (can be fractional).| |`SOUNDEX(str)`|Returns the soundex of a string.| |`SPLIT(str,sep)`|Receives a string and a separator and returns the parts of the string split by the separator as a JSON array of strings.| |`SQRT(X)`|Returns the square root of a nonnegative number X.| |`SUBSTR(str, pos, [len])`|Return a substring from the provided string starting at `pos` with a length of `len` characters. If no `len` is provided, all characters from `pos` until the end will be taken.| |`SUBSTRING(str, pos, [len])`|Return a substring from the provided string starting at `pos` with a length of `len` characters. If no `len` is provided, all characters from `pos` until the end will be taken.| |`SUM(expr)`|Returns the sum of expr in all rows.| +|`TO_BASE64(str)`|Encodes the string str in base64 format.| +|`FROM_BASE64(str)`|Decodes the base64-encoded string str.| |`TRIM(str)`|Returns the string str with all spaces removed.| |`UPPER(str)`|Returns the string str with all characters in upper case.| |`WEEKDAY(date)`|Returns the weekday of the given date.| |`YEAR(date)`|Returns the year of the given date.| +|`YEARWEEK(date, mode)`|Returns year and week for a date. The year in the result may be different from the year in the date argument for the first and the last week of the year.| ## Configuration diff --git a/vendor/gopkg.in/src-d/go-mysql-server.v0/SUPPORTED.md b/vendor/gopkg.in/src-d/go-mysql-server.v0/SUPPORTED.md index 84d33a0e0..a6f57ad5a 100644 --- a/vendor/gopkg.in/src-d/go-mysql-server.v0/SUPPORTED.md +++ b/vendor/gopkg.in/src-d/go-mysql-server.v0/SUPPORTED.md @@ -50,6 +50,7 @@ - USE - SHOW DATABASES - SHOW WARNINGS +- INTERVALS ## Index expressions - CREATE INDEX (an index can be created using either column names or a single arbitrary expression). @@ -67,8 +68,8 @@ - OR ## Arithmetic expressions -- \+ -- \- +- \+ (including between dates and intervals) +- \- (including between dates and intervals) - \* - \\ - << @@ -109,10 +110,15 @@ - LN - LOG2 - LOG10 +- SLEEP +- TO_BASE64 +- FROM_BASE64 ## Time functions +- DATE - DAY - WEEKDAY +- DAYOFMONTH - DAYOFWEEK - DAYOFYEAR - HOUR @@ -120,4 +126,7 @@ - MONTH - SECOND - YEAR +- YEARWEEK - NOW +- DATE_ADD +- DATE_SUB diff --git a/vendor/gopkg.in/src-d/go-mysql-server.v0/server/handler.go b/vendor/gopkg.in/src-d/go-mysql-server.v0/server/handler.go index 73dfec845..a863704bc 100644 --- a/vendor/gopkg.in/src-d/go-mysql-server.v0/server/handler.go +++ b/vendor/gopkg.in/src-d/go-mysql-server.v0/server/handler.go @@ -221,11 +221,9 @@ func rowToSQL(s sql.Schema, row sql.Row) []sqltypes.Value { func schemaToFields(s sql.Schema) []*query.Field { fields := make([]*query.Field, len(s)) for i, c := range s { - var charset uint32 - if c.Type.Type() == mysql.TypeBlob { + var charset uint32 = mysql.CharacterSetUtf8 + if c.Type == sql.Blob { charset = mysql.CharacterSetBinary - } else { - charset = mysql.CharacterSetUtf8 } fields[i] = &query.Field{ diff --git a/vendor/gopkg.in/src-d/go-mysql-server.v0/sql/analyzer/validation_rules.go b/vendor/gopkg.in/src-d/go-mysql-server.v0/sql/analyzer/validation_rules.go index 500b3608c..c1cbadcc5 100644 --- a/vendor/gopkg.in/src-d/go-mysql-server.v0/sql/analyzer/validation_rules.go +++ b/vendor/gopkg.in/src-d/go-mysql-server.v0/sql/analyzer/validation_rules.go @@ -6,6 +6,7 @@ import ( errors "gopkg.in/src-d/go-errors.v1" "gopkg.in/src-d/go-mysql-server.v0/sql" "gopkg.in/src-d/go-mysql-server.v0/sql/expression" + "gopkg.in/src-d/go-mysql-server.v0/sql/expression/function" "gopkg.in/src-d/go-mysql-server.v0/sql/plan" ) @@ -17,6 +18,7 @@ const ( validateProjectTuplesRule = "validate_project_tuples" validateIndexCreationRule = "validate_index_creation" validateCaseResultTypesRule = "validate_case_result_types" + validateIntervalUsageRule = "validate_interval_usage" ) var ( @@ -43,6 +45,12 @@ var ( "expecting all case branches to return values of type %s, " + "but found value %q of type %s on %s", ) + // ErrIntervalInvalidUse is returned when an interval expression is not + // correctly used. + ErrIntervalInvalidUse = errors.NewKind( + "invalid use of an interval, which can only be used with DATE_ADD, " + + "DATE_SUB and +/- operators to subtract from or add to a date", + ) ) // DefaultValidationRules to apply while analyzing nodes. @@ -54,6 +62,7 @@ var DefaultValidationRules = []Rule{ {validateProjectTuplesRule, validateProjectTuples}, {validateIndexCreationRule, validateIndexCreation}, {validateCaseResultTypesRule, validateCaseResultTypes}, + {validateIntervalUsageRule, validateIntervalUsage}, } func validateIsResolved(ctx *sql.Context, a *Analyzer, n sql.Node) (sql.Node, error) { @@ -243,6 +252,35 @@ func validateCaseResultTypes(ctx *sql.Context, a *Analyzer, n sql.Node) (sql.Nod return n, nil } +func validateIntervalUsage(ctx *sql.Context, a *Analyzer, n sql.Node) (sql.Node, error) { + var invalid bool + plan.InspectExpressions(n, func(e sql.Expression) bool { + // If it's already invalid just skip everything else. + if invalid { + return false + } + + switch e := e.(type) { + case *function.DateAdd, *function.DateSub: + return false + case *expression.Arithmetic: + if e.Op == "+" || e.Op == "-" { + return false + } + case *expression.Interval: + invalid = true + } + + return true + }) + + if invalid { + return nil, ErrIntervalInvalidUse.New() + } + + return n, nil +} + func stringContains(strs []string, target string) bool { for _, s := range strs { if s == target { diff --git a/vendor/gopkg.in/src-d/go-mysql-server.v0/sql/expression/arithmetic.go b/vendor/gopkg.in/src-d/go-mysql-server.v0/sql/expression/arithmetic.go index 005bd6260..a0ccfe2e5 100644 --- a/vendor/gopkg.in/src-d/go-mysql-server.v0/sql/expression/arithmetic.go +++ b/vendor/gopkg.in/src-d/go-mysql-server.v0/sql/expression/arithmetic.go @@ -3,6 +3,7 @@ package expression import ( "fmt" "reflect" + "time" errors "gopkg.in/src-d/go-errors.v1" "gopkg.in/src-d/go-vitess.v1/vt/sqlparser" @@ -21,7 +22,7 @@ var ( // Arithmetic expressions (+, -, *, /, ...) type Arithmetic struct { BinaryExpression - op string + Op string } // NewArithmetic creates a new Arithmetic sql.Expression. @@ -85,13 +86,19 @@ func NewMod(left, right sql.Expression) *Arithmetic { } func (a *Arithmetic) String() string { - return fmt.Sprintf("%s %s %s", a.Left, a.op, a.Right) + return fmt.Sprintf("%s %s %s", a.Left, a.Op, a.Right) } // Type returns the greatest type for given operation. func (a *Arithmetic) Type() sql.Type { - switch a.op { + switch a.Op { case sqlparser.PlusStr, sqlparser.MinusStr, sqlparser.MultStr, sqlparser.DivStr: + _, lok := a.Left.(*Interval) + _, rok := a.Right.(*Interval) + if lok || rok { + return sql.Timestamp + } + if sql.IsInteger(a.Left.Type()) && sql.IsInteger(a.Right.Type()) { if sql.IsUnsigned(a.Left.Type()) && sql.IsUnsigned(a.Right.Type()) { return sql.Uint64 @@ -126,7 +133,7 @@ func (a *Arithmetic) TransformUp(f sql.TransformExprFunc) (sql.Expression, error return nil, err } - return f(NewArithmetic(l, r, a.op)) + return f(NewArithmetic(l, r, a.Op)) } // Eval implements the Expression interface. @@ -141,7 +148,7 @@ func (a *Arithmetic) Eval(ctx *sql.Context, row sql.Row) (interface{}, error) { return nil, err } - switch a.op { + switch a.Op { case sqlparser.PlusStr: return plus(lval, rval) case sqlparser.MinusStr: @@ -166,37 +173,63 @@ func (a *Arithmetic) Eval(ctx *sql.Context, row sql.Row) (interface{}, error) { return mod(lval, rval) } - return nil, errUnableToEval.New(lval, a.op, rval) + return nil, errUnableToEval.New(lval, a.Op, rval) } func (a *Arithmetic) evalLeftRight(ctx *sql.Context, row sql.Row) (interface{}, interface{}, error) { - lval, err := a.Left.Eval(ctx, row) - if err != nil { - return nil, nil, err + var lval, rval interface{} + var err error + + if i, ok := a.Left.(*Interval); ok { + lval, err = i.EvalDelta(ctx, row) + if err != nil { + return nil, nil, err + } + } else { + lval, err = a.Left.Eval(ctx, row) + if err != nil { + return nil, nil, err + } } - rval, err := a.Right.Eval(ctx, row) - if err != nil { - return nil, nil, err + if i, ok := a.Right.(*Interval); ok { + rval, err = i.EvalDelta(ctx, row) + if err != nil { + return nil, nil, err + } + } else { + rval, err = a.Right.Eval(ctx, row) + if err != nil { + return nil, nil, err + } } return lval, rval, nil } -func (a *Arithmetic) convertLeftRight(lval interface{}, rval interface{}) (interface{}, interface{}, error) { +func (a *Arithmetic) convertLeftRight(left interface{}, right interface{}) (interface{}, interface{}, error) { + var err error typ := a.Type() - lval64, err := typ.Convert(lval) - if err != nil { - return nil, nil, err + if i, ok := left.(*TimeDelta); ok { + left = i + } else { + left, err = typ.Convert(left) + if err != nil { + return nil, nil, err + } } - rval64, err := typ.Convert(rval) - if err != nil { - return nil, nil, err + if i, ok := right.(*TimeDelta); ok { + right = i + } else { + right, err = typ.Convert(right) + if err != nil { + return nil, nil, err + } } - return lval64, rval64, nil + return left, right, nil } func plus(lval, rval interface{}) (interface{}, error) { @@ -218,6 +251,16 @@ func plus(lval, rval interface{}) (interface{}, error) { case float64: return l + r, nil } + case time.Time: + switch r := rval.(type) { + case *TimeDelta: + return r.Add(l), nil + } + case *TimeDelta: + switch r := rval.(type) { + case time.Time: + return l.Add(r), nil + } } return nil, errUnableToCast.New(lval, rval) @@ -242,6 +285,11 @@ func minus(lval, rval interface{}) (interface{}, error) { case float64: return l - r, nil } + case time.Time: + switch r := rval.(type) { + case *TimeDelta: + return r.Sub(l), nil + } } return nil, errUnableToCast.New(lval, rval) diff --git a/vendor/gopkg.in/src-d/go-mysql-server.v0/sql/expression/function/date.go b/vendor/gopkg.in/src-d/go-mysql-server.v0/sql/expression/function/date.go new file mode 100644 index 000000000..116eaead6 --- /dev/null +++ b/vendor/gopkg.in/src-d/go-mysql-server.v0/sql/expression/function/date.go @@ -0,0 +1,177 @@ +package function + +import ( + "fmt" + "time" + + "gopkg.in/src-d/go-mysql-server.v0/sql" + "gopkg.in/src-d/go-mysql-server.v0/sql/expression" +) + +// DateAdd adds an interval to a date. +type DateAdd struct { + Date sql.Expression + Interval *expression.Interval +} + +// NewDateAdd creates a new date add function. +func NewDateAdd(args ...sql.Expression) (sql.Expression, error) { + if len(args) != 2 { + return nil, sql.ErrInvalidArgumentNumber.New("DATE_ADD", 2, len(args)) + } + + i, ok := args[1].(*expression.Interval) + if !ok { + return nil, fmt.Errorf("DATE_ADD expects an interval as second parameter") + } + + return &DateAdd{args[0], i}, nil +} + +// Children implements the sql.Expression interface. +func (d *DateAdd) Children() []sql.Expression { + return []sql.Expression{d.Date, d.Interval} +} + +// Resolved implements the sql.Expression interface. +func (d *DateAdd) Resolved() bool { + return d.Date.Resolved() && d.Interval.Resolved() +} + +// IsNullable implements the sql.Expression interface. +func (d *DateAdd) IsNullable() bool { + return d.Date.IsNullable() || d.Interval.IsNullable() +} + +// Type implements the sql.Expression interface. +func (d *DateAdd) Type() sql.Type { return sql.Date } + +// TransformUp implements the sql.Expression interface. +func (d *DateAdd) TransformUp(f sql.TransformExprFunc) (sql.Expression, error) { + date, err := d.Date.TransformUp(f) + if err != nil { + return nil, err + } + interval, err := d.Interval.TransformUp(f) + if err != nil { + return nil, err + } + + return &DateAdd{date, interval.(*expression.Interval)}, nil +} + +// Eval implements the sql.Expression interface. +func (d *DateAdd) Eval(ctx *sql.Context, row sql.Row) (interface{}, error) { + date, err := d.Date.Eval(ctx, row) + if err != nil { + return nil, err + } + + if date == nil { + return nil, nil + } + + date, err = sql.Timestamp.Convert(date) + if err != nil { + return nil, err + } + + delta, err := d.Interval.EvalDelta(ctx, row) + if err != nil { + return nil, err + } + + if delta == nil { + return nil, nil + } + + return delta.Add(date.(time.Time)), nil +} + +func (d *DateAdd) String() string { + return fmt.Sprintf("DATE_ADD(%s, %s)", d.Date, d.Interval) +} + +// DateSub subtracts an interval from a date. +type DateSub struct { + Date sql.Expression + Interval *expression.Interval +} + +// NewDateSub creates a new date add function. +func NewDateSub(args ...sql.Expression) (sql.Expression, error) { + if len(args) != 2 { + return nil, sql.ErrInvalidArgumentNumber.New("DATE_SUB", 2, len(args)) + } + + i, ok := args[1].(*expression.Interval) + if !ok { + return nil, fmt.Errorf("DATE_SUB expects an interval as second parameter") + } + + return &DateSub{args[0], i}, nil +} + +// Children implements the sql.Expression interface. +func (d *DateSub) Children() []sql.Expression { + return []sql.Expression{d.Date, d.Interval} +} + +// Resolved implements the sql.Expression interface. +func (d *DateSub) Resolved() bool { + return d.Date.Resolved() && d.Interval.Resolved() +} + +// IsNullable implements the sql.Expression interface. +func (d *DateSub) IsNullable() bool { + return d.Date.IsNullable() || d.Interval.IsNullable() +} + +// Type implements the sql.Expression interface. +func (d *DateSub) Type() sql.Type { return sql.Date } + +// TransformUp implements the sql.Expression interface. +func (d *DateSub) TransformUp(f sql.TransformExprFunc) (sql.Expression, error) { + date, err := d.Date.TransformUp(f) + if err != nil { + return nil, err + } + interval, err := d.Interval.TransformUp(f) + if err != nil { + return nil, err + } + + return &DateSub{date, interval.(*expression.Interval)}, nil +} + +// Eval implements the sql.Expression interface. +func (d *DateSub) Eval(ctx *sql.Context, row sql.Row) (interface{}, error) { + date, err := d.Date.Eval(ctx, row) + if err != nil { + return nil, err + } + + if date == nil { + return nil, nil + } + + date, err = sql.Timestamp.Convert(date) + if err != nil { + return nil, err + } + + delta, err := d.Interval.EvalDelta(ctx, row) + if err != nil { + return nil, err + } + + if delta == nil { + return nil, nil + } + + return delta.Sub(date.(time.Time)), nil +} + +func (d *DateSub) String() string { + return fmt.Sprintf("DATE_SUB(%s, %s)", d.Date, d.Interval) +} diff --git a/vendor/gopkg.in/src-d/go-mysql-server.v0/sql/expression/function/registry.go b/vendor/gopkg.in/src-d/go-mysql-server.v0/sql/expression/function/registry.go index 4248e6874..87a33d676 100644 --- a/vendor/gopkg.in/src-d/go-mysql-server.v0/sql/expression/function/registry.go +++ b/vendor/gopkg.in/src-d/go-mysql-server.v0/sql/expression/function/registry.go @@ -33,6 +33,7 @@ var Defaults = []sql.Function{ sql.FunctionN{Name: "substring", Fn: NewSubstring}, sql.FunctionN{Name: "mid", Fn: NewSubstring}, sql.FunctionN{Name: "substr", Fn: NewSubstring}, + sql.Function1{Name: "date", Fn: NewDate}, sql.Function1{Name: "year", Fn: NewYear}, sql.Function1{Name: "month", Fn: NewMonth}, sql.Function1{Name: "day", Fn: NewDay}, @@ -41,7 +42,9 @@ var Defaults = []sql.Function{ sql.Function1{Name: "minute", Fn: NewMinute}, sql.Function1{Name: "second", Fn: NewSecond}, sql.Function1{Name: "dayofweek", Fn: NewDayOfWeek}, + sql.Function1{Name: "dayofmonth", Fn: NewDay}, sql.Function1{Name: "dayofyear", Fn: NewDayOfYear}, + sql.FunctionN{Name: "yearweek", Fn: NewYearWeek}, sql.Function1{Name: "array_length", Fn: NewArrayLength}, sql.Function2{Name: "split", Fn: NewSplit}, sql.FunctionN{Name: "concat", Fn: NewConcat}, @@ -74,4 +77,9 @@ var Defaults = []sql.Function{ sql.Function2{Name: "ifnull", Fn: NewIfNull}, sql.Function2{Name: "nullif", Fn: NewNullIf}, sql.Function0{Name: "now", Fn: NewNow}, + sql.Function1{Name: "sleep", Fn: NewSleep}, + sql.Function1{Name: "to_base64", Fn: NewToBase64}, + sql.Function1{Name: "from_base64", Fn: NewFromBase64}, + sql.FunctionN{Name: "date_add", Fn: NewDateAdd}, + sql.FunctionN{Name: "date_sub", Fn: NewDateSub}, } diff --git a/vendor/gopkg.in/src-d/go-mysql-server.v0/sql/expression/function/sleep.go b/vendor/gopkg.in/src-d/go-mysql-server.v0/sql/expression/function/sleep.go new file mode 100644 index 000000000..61aa12f02 --- /dev/null +++ b/vendor/gopkg.in/src-d/go-mysql-server.v0/sql/expression/function/sleep.go @@ -0,0 +1,66 @@ +package function + +import ( + "fmt" + "time" + + "gopkg.in/src-d/go-mysql-server.v0/sql" + "gopkg.in/src-d/go-mysql-server.v0/sql/expression" +) + +// Sleep is a function that just waits for the specified number of seconds +// and returns 0. +// It can be useful to test timeouts or long queries. +type Sleep struct { + expression.UnaryExpression +} + +// NewSleep creates a new Sleep expression. +func NewSleep(e sql.Expression) sql.Expression { + return &Sleep{expression.UnaryExpression{Child: e}} +} + +// Eval implements the Expression interface. +func (s *Sleep) Eval(ctx *sql.Context, row sql.Row) (interface{}, error) { + child, err := s.Child.Eval(ctx, row) + + if err != nil { + return nil, err + } + + if child == nil { + return nil, nil + } + + child, err = sql.Float64.Convert(child) + if err != nil { + return nil, err + } + + time.Sleep(time.Duration(child.(float64) * 1000) * time.Millisecond) + return 0, nil +} + +// String implements the Stringer interface. +func (s *Sleep) String() string { + return fmt.Sprintf("SLEEP(%s)", s.Child) +} + +// IsNullable implements the Expression interface. +func (s *Sleep) IsNullable() bool { + return false +} + +// TransformUp implements the Expression interface. +func (s *Sleep) TransformUp(f sql.TransformExprFunc) (sql.Expression, error) { + child, err := s.Child.TransformUp(f) + if err != nil { + return nil, err + } + return f(NewSleep(child)) +} + +// Type implements the Expression interface. +func (s *Sleep) Type() sql.Type { + return sql.Int32 +} diff --git a/vendor/gopkg.in/src-d/go-mysql-server.v0/sql/expression/function/time.go b/vendor/gopkg.in/src-d/go-mysql-server.v0/sql/expression/function/time.go index 3f8594fdd..95b37c9a0 100644 --- a/vendor/gopkg.in/src-d/go-mysql-server.v0/sql/expression/function/time.go +++ b/vendor/gopkg.in/src-d/go-mysql-server.v0/sql/expression/function/time.go @@ -1,6 +1,7 @@ package function import ( + "errors" "fmt" "time" @@ -8,12 +9,10 @@ import ( "gopkg.in/src-d/go-mysql-server.v0/sql/expression" ) -func getDatePart( - ctx *sql.Context, +func getDate(ctx *sql.Context, u expression.UnaryExpression, - row sql.Row, - f func(interface{}) interface{}, -) (interface{}, error) { + row sql.Row) (interface{}, error) { + val, err := u.Child.Eval(ctx, row) if err != nil { return nil, err @@ -31,6 +30,19 @@ func getDatePart( } } + return date, nil +} + +func getDatePart(ctx *sql.Context, + u expression.UnaryExpression, + row sql.Row, + f func(interface{}) interface{}) (interface{}, error) { + + date, err := getDate(ctx, u, row) + if err != nil { + return nil, err + } + return f(date), nil } @@ -316,6 +328,202 @@ func datePartFunc(fn func(time.Time) int) func(interface{}) interface{} { } } +// YearWeek is a function that returns year and week for a date. +// The year in the result may be different from the year in the date argument for the first and the last week of the year. +// Details: https://dev.mysql.com/doc/refman/5.5/en/date-and-time-functions.html#function_yearweek +type YearWeek struct { + date sql.Expression + mode sql.Expression +} + +// NewYearWeek creates a new YearWeek UDF +func NewYearWeek(args ...sql.Expression) (sql.Expression, error) { + if len(args) == 0 { + return nil, sql.ErrInvalidArgumentNumber.New("YEARWEEK", "1 or more", 0) + } + + yw := &YearWeek{date: args[0]} + if len(args) > 1 && args[1].Resolved() && sql.IsInteger(args[1].Type()) { + yw.mode = args[1] + } else { + yw.mode = expression.NewLiteral(0, sql.Int64) + } + return yw, nil +} + +func (d *YearWeek) String() string { return fmt.Sprintf("YEARWEEK(%s, %d)", d.date, d.mode) } + +// Type implements the Expression interface. +func (d *YearWeek) Type() sql.Type { return sql.Int32 } + +// Eval implements the Expression interface. +func (d *YearWeek) Eval(ctx *sql.Context, row sql.Row) (interface{}, error) { + date, err := getDate(ctx, expression.UnaryExpression{Child: d.date}, row) + if err != nil { + return nil, err + } + yyyy, ok := year(date).(int32) + if !ok { + return nil, errors.New("YEARWEEK: invalid year") + } + mm, ok := month(date).(int32) + if !ok { + return nil, errors.New("YEARWEEK: invalid month") + } + dd, ok := day(date).(int32) + if !ok { + return nil, errors.New("YEARWEEK: invalid day") + } + + fmt.Println(yyyy, mm, dd) + + mode := int64(0) + val, err := d.mode.Eval(ctx, row) + if err != nil { + return nil, err + } + if val != nil { + if mode, ok = val.(int64); ok { + mode %= 8 // mode in [0, 7] + } + } + yyyy, week := calcWeek(yyyy, mm, dd, weekMode(mode)|weekBehaviourYear) + + return (yyyy * 100) + week, nil +} + +// Resolved implements the Expression interface. +func (d *YearWeek) Resolved() bool { + return d.date.Resolved() && d.mode.Resolved() +} + +// Children implements the Expression interface. +func (d *YearWeek) Children() []sql.Expression { return []sql.Expression{d.date, d.mode} } + +// IsNullable implements the Expression interface. +func (d *YearWeek) IsNullable() bool { + return d.date.IsNullable() +} + +// TransformUp implements the Expression interface. +func (d *YearWeek) TransformUp(f sql.TransformExprFunc) (sql.Expression, error) { + date, err := d.date.TransformUp(f) + if err != nil { + return nil, err + } + + mode, err := d.mode.TransformUp(f) + if err != nil { + return nil, err + } + + yw, err := NewYearWeek(date, mode) + if err != nil { + return nil, err + } + return f(yw) +} + +// Following solution of YearWeek was taken from tidb: https://github.com/pingcap/tidb/blob/master/types/mytime.go +type weekBehaviour int64 + +const ( + // weekBehaviourMondayFirst set Monday as first day of week; otherwise Sunday is first day of week + weekBehaviourMondayFirst weekBehaviour = 1 << iota + // If set, Week is in range 1-53, otherwise Week is in range 0-53. + // Note that this flag is only relevant if WEEK_JANUARY is not set. + weekBehaviourYear + // If not set, Weeks are numbered according to ISO 8601:1988. + // If set, the week that contains the first 'first-day-of-week' is week 1. + weekBehaviourFirstWeekday +) + +func (v weekBehaviour) test(flag weekBehaviour) bool { + return (v & flag) != 0 +} + +func weekMode(mode int64) weekBehaviour { + weekFormat := weekBehaviour(mode & 7) + if (weekFormat & weekBehaviourMondayFirst) == 0 { + weekFormat ^= weekBehaviourFirstWeekday + } + return weekFormat +} + +// calcWeekday calculates weekday from daynr, returns 0 for Monday, 1 for Tuesday ... +func calcWeekday(daynr int32, sundayFirstDayOfWeek bool) int32 { + daynr += 5 + if sundayFirstDayOfWeek { + daynr++ + } + return daynr % 7 +} + +// calcWeek calculates week and year for the time. +func calcWeek(yyyy, mm, dd int32, wb weekBehaviour) (int32, int32) { + daynr := calcDaynr(yyyy, mm, dd) + firstDaynr := calcDaynr(yyyy, 1, 1) + mondayFirst := wb.test(weekBehaviourMondayFirst) + weekYear := wb.test(weekBehaviourYear) + firstWeekday := wb.test(weekBehaviourFirstWeekday) + weekday := calcWeekday(firstDaynr, !mondayFirst) + + week, days := int32(0), int32(0) + if mm == 1 && dd <= 7-weekday { + if !weekYear && + ((firstWeekday && weekday != 0) || (!firstWeekday && weekday >= 4)) { + return yyyy, week + } + weekYear = true + yyyy-- + days = calcDaysInYear(yyyy) + firstDaynr -= days + weekday = (weekday + 53*7 - days) % 7 + } + + if (firstWeekday && weekday != 0) || + (!firstWeekday && weekday >= 4) { + days = daynr - (firstDaynr + 7 - weekday) + } else { + days = daynr - (firstDaynr - weekday) + } + + if weekYear && days >= 52*7 { + weekday = (weekday + calcDaysInYear(yyyy)) % 7 + if (!firstWeekday && weekday < 4) || + (firstWeekday && weekday == 0) { + yyyy++ + week = 1 + return yyyy, week + } + } + week = days/7 + 1 + return yyyy, week +} + +// calcDaysInYear calculates days in one year, it works with 0 <= yyyy <= 99. +func calcDaysInYear(yyyy int32) int32 { + if (yyyy&3) == 0 && (yyyy%100 != 0 || (yyyy%400 == 0 && (yyyy != 0))) { + return 366 + } + return 365 +} + +// calcDaynr calculates days since 0000-00-00. +func calcDaynr(yyyy, mm, dd int32) int32 { + if yyyy == 0 && mm == 0 { + return 0 + } + + delsum := 365*yyyy + 31*(mm-1) + dd + if mm <= 2 { + yyyy-- + } else { + delsum -= (mm*4 + 23) / 10 + } + return delsum + yyyy/4 - ((yyyy/100+1)*3)/4 +} + var ( year = datePartFunc((time.Time).Year) month = datePartFunc(func(t time.Time) int { return int(t.Month()) }) @@ -365,3 +573,39 @@ func (n *Now) Eval(*sql.Context, sql.Row) (interface{}, error) { func (n *Now) TransformUp(f sql.TransformExprFunc) (sql.Expression, error) { return f(n) } + +// Date a function takes the DATE part out from a datetime expression. +type Date struct { + expression.UnaryExpression +} + +// NewDate returns a new Date node. +func NewDate(date sql.Expression) sql.Expression { + return &Date{expression.UnaryExpression{Child: date}} +} + +func (d *Date) String() string { return fmt.Sprintf("DATE(%s)", d.Child) } + +// Type implements the Expression interface. +func (d *Date) Type() sql.Type { return sql.Text } + +// Eval implements the Expression interface. +func (d *Date) Eval(ctx *sql.Context, row sql.Row) (interface{}, error) { + return getDatePart(ctx, d.UnaryExpression, row, func(v interface{}) interface{} { + if v == nil { + return nil + } + + return v.(time.Time).Format("2006-01-02") + }) +} + +// TransformUp implements the sql.Expression interface. +func (d *Date) TransformUp(f sql.TransformExprFunc) (sql.Expression, error) { + child, err := d.Child.TransformUp(f) + if err != nil { + return nil, err + } + + return f(NewDate(child)) +} diff --git a/vendor/gopkg.in/src-d/go-mysql-server.v0/sql/expression/function/tobase64_frombase64.go b/vendor/gopkg.in/src-d/go-mysql-server.v0/sql/expression/function/tobase64_frombase64.go new file mode 100644 index 000000000..c6d484ef2 --- /dev/null +++ b/vendor/gopkg.in/src-d/go-mysql-server.v0/sql/expression/function/tobase64_frombase64.go @@ -0,0 +1,148 @@ +package function + +import ( + "encoding/base64" + "fmt" + "reflect" + "strings" + + "gopkg.in/src-d/go-mysql-server.v0/sql" + "gopkg.in/src-d/go-mysql-server.v0/sql/expression" +) + +// ToBase64 is a function to encode a string to the Base64 format +// using the same dialect that MySQL's TO_BASE64 uses +type ToBase64 struct { + expression.UnaryExpression +} + +// NewToBase64 creates a new ToBase64 expression. +func NewToBase64(e sql.Expression) sql.Expression { + return &ToBase64{expression.UnaryExpression{Child: e}} +} + +// Eval implements the Expression interface. +func (t *ToBase64) Eval(ctx *sql.Context, row sql.Row) (interface{}, error) { + str, err := t.Child.Eval(ctx, row) + + if err != nil { + return nil, err + } + + if str == nil { + return nil, nil + } + + str, err = sql.Text.Convert(str) + if err != nil { + return nil, sql.ErrInvalidType.New(reflect.TypeOf(str)) + } + + encoded := base64.StdEncoding.EncodeToString([]byte(str.(string))) + + lenEncoded := len(encoded) + if lenEncoded <= 76 { + return encoded, nil + } + + // Split into max 76 chars lines + var out strings.Builder + start := 0 + end := 76 + for { + out.WriteString(encoded[start:end] + "\n") + start += 76 + end += 76 + if end >= lenEncoded { + out.WriteString(encoded[start:lenEncoded]) + break + } + } + + return out.String(), nil +} + +// String implements the Stringer interface. +func (t *ToBase64) String() string { + return fmt.Sprintf("TO_BASE64(%s)", t.Child) +} + +// IsNullable implements the Expression interface. +func (t *ToBase64) IsNullable() bool { + return t.Child.IsNullable() +} + +// TransformUp implements the Expression interface. +func (t *ToBase64) TransformUp(f sql.TransformExprFunc) (sql.Expression, error) { + child, err := t.Child.TransformUp(f) + if err != nil { + return nil, err + } + return f(NewToBase64(child)) +} + +// Type implements the Expression interface. +func (t *ToBase64) Type() sql.Type { + return sql.Text +} + + +// FromBase64 is a function to decode a Base64-formatted string +// using the same dialect that MySQL's FROM_BASE64 uses +type FromBase64 struct { + expression.UnaryExpression +} + +// NewFromBase64 creates a new FromBase64 expression. +func NewFromBase64(e sql.Expression) sql.Expression { + return &FromBase64{expression.UnaryExpression{Child: e}} +} + +// Eval implements the Expression interface. +func (t *FromBase64) Eval(ctx *sql.Context, row sql.Row) (interface{}, error) { + str, err := t.Child.Eval(ctx, row) + + if err != nil { + return nil, err + } + + if str == nil { + return nil, nil + } + + str, err = sql.Text.Convert(str) + if err != nil { + return nil, sql.ErrInvalidType.New(reflect.TypeOf(str)) + } + + decoded, err := base64.StdEncoding.DecodeString(str.(string)) + if err != nil { + return nil, err + } + + return string(decoded), nil +} + +// String implements the Stringer interface. +func (t *FromBase64) String() string { + return fmt.Sprintf("FROM_BASE64(%s)", t.Child) +} + +// IsNullable implements the Expression interface. +func (t *FromBase64) IsNullable() bool { + return t.Child.IsNullable() +} + +// TransformUp implements the Expression interface. +func (t *FromBase64) TransformUp(f sql.TransformExprFunc) (sql.Expression, error) { + child, err := t.Child.TransformUp(f) + if err != nil { + return nil, err + } + return f(NewFromBase64(child)) +} + +// Type implements the Expression interface. +func (t *FromBase64) Type() sql.Type { + return sql.Text +} diff --git a/vendor/gopkg.in/src-d/go-mysql-server.v0/sql/expression/interval.go b/vendor/gopkg.in/src-d/go-mysql-server.v0/sql/expression/interval.go new file mode 100644 index 000000000..0053b8391 --- /dev/null +++ b/vendor/gopkg.in/src-d/go-mysql-server.v0/sql/expression/interval.go @@ -0,0 +1,286 @@ +package expression + +import ( + "fmt" + "regexp" + "strconv" + "strings" + "time" + + errors "gopkg.in/src-d/go-errors.v1" + "gopkg.in/src-d/go-mysql-server.v0/sql" +) + +// Interval defines a time duration. +type Interval struct { + UnaryExpression + Unit string +} + +// NewInterval creates a new interval expression. +func NewInterval(child sql.Expression, unit string) *Interval { + return &Interval{UnaryExpression{Child: child}, strings.ToUpper(unit)} +} + +// Type implements the sql.Expression interface. +func (i *Interval) Type() sql.Type { return sql.Uint64 } + +// IsNullable implements the sql.Expression interface. +func (i *Interval) IsNullable() bool { return i.Child.IsNullable() } + +// Eval implements the sql.Expression interface. +func (i *Interval) Eval(ctx *sql.Context, row sql.Row) (interface{}, error) { + panic("Interval.Eval is just a placeholder method and should not be called directly") +} + +var ( + errInvalidIntervalUnit = errors.NewKind("invalid interval unit: %s") + errInvalidIntervalFormat = errors.NewKind("invalid interval format for %q: %s") +) + +// EvalDelta evaluates the expression returning a TimeDelta. This method should +// be used instead of Eval, as this expression returns a TimeDelta, which is not +// a valid value that can be returned in Eval. +func (i *Interval) EvalDelta(ctx *sql.Context, row sql.Row) (*TimeDelta, error) { + val, err := i.Child.Eval(ctx, row) + if err != nil { + return nil, err + } + + if val == nil { + return nil, nil + } + + var td TimeDelta + + if r, ok := unitTextFormats[i.Unit]; ok { + val, err = sql.Text.Convert(val) + if err != nil { + return nil, err + } + + text := val.(string) + if !r.MatchString(text) { + return nil, errInvalidIntervalFormat.New(i.Unit, text) + } + + parts := textFormatParts(text, r) + + switch i.Unit { + case "DAY_HOUR": + td.Days = parts[0] + td.Hours = parts[1] + case "DAY_MICROSECOND": + td.Days = parts[0] + td.Hours = parts[1] + td.Minutes = parts[2] + td.Seconds = parts[3] + td.Microseconds = parts[4] + case "DAY_MINUTE": + td.Days = parts[0] + td.Hours = parts[1] + td.Minutes = parts[2] + case "DAY_SECOND": + td.Days = parts[0] + td.Hours = parts[1] + td.Minutes = parts[2] + td.Seconds = parts[3] + case "HOUR_MICROSECOND": + td.Hours = parts[0] + td.Minutes = parts[1] + td.Seconds = parts[2] + td.Microseconds = parts[3] + case "HOUR_SECOND": + td.Hours = parts[0] + td.Minutes = parts[1] + td.Seconds = parts[2] + case "HOUR_MINUTE": + td.Hours = parts[0] + td.Minutes = parts[1] + case "MINUTE_MICROSECOND": + td.Minutes = parts[0] + td.Seconds = parts[1] + td.Microseconds = parts[2] + case "MINUTE_SECOND": + td.Minutes = parts[0] + td.Seconds = parts[1] + case "SECOND_MICROSECOND": + td.Seconds = parts[0] + td.Microseconds = parts[1] + case "YEAR_MONTH": + td.Years = parts[0] + td.Months = parts[1] + default: + return nil, errInvalidIntervalUnit.New(i.Unit) + } + } else { + val, err = sql.Int64.Convert(val) + if err != nil { + return nil, err + } + + num := val.(int64) + + switch i.Unit { + case "DAY": + td.Days = num + case "HOUR": + td.Hours = num + case "MINUTE": + td.Minutes = num + case "SECOND": + td.Seconds = num + case "MICROSECOND": + td.Microseconds = num + case "QUARTER": + td.Months = num * 3 + case "MONTH": + td.Months = num + case "WEEK": + td.Days = num * 7 + case "YEAR": + td.Years = num + default: + return nil, errInvalidIntervalUnit.New(i.Unit) + } + } + + return &td, nil +} + +// TransformUp implements the sql.Expression interface. +func (i *Interval) TransformUp(f sql.TransformExprFunc) (sql.Expression, error) { + child, err := i.Child.TransformUp(f) + if err != nil { + return nil, err + } + + return NewInterval(child, i.Unit), nil +} + +func (i *Interval) String() string { + return fmt.Sprintf("INTERVAL %s %s", i.Child, i.Unit) +} + +var unitTextFormats = map[string]*regexp.Regexp{ + "DAY_HOUR": regexp.MustCompile(`^(\d+)\s+(\d+)$`), + "DAY_MICROSECOND": regexp.MustCompile(`^(\d+)\s+(\d+):(\d+):(\d+).(\d+)$`), + "DAY_MINUTE": regexp.MustCompile(`^(\d+)\s+(\d+):(\d+)$`), + "DAY_SECOND": regexp.MustCompile(`^(\d+)\s+(\d+):(\d+):(\d+)$`), + "HOUR_MICROSECOND": regexp.MustCompile(`^(\d+):(\d+):(\d+).(\d+)$`), + "HOUR_SECOND": regexp.MustCompile(`^(\d+):(\d+):(\d+)$`), + "HOUR_MINUTE": regexp.MustCompile(`^(\d+):(\d+)$`), + "MINUTE_MICROSECOND": regexp.MustCompile(`^(\d+):(\d+).(\d+)$`), + "MINUTE_SECOND": regexp.MustCompile(`^(\d+):(\d+)$`), + "SECOND_MICROSECOND": regexp.MustCompile(`^(\d+).(\d+)$`), + "YEAR_MONTH": regexp.MustCompile(`^(\d+)-(\d+)$`), +} + +func textFormatParts(text string, r *regexp.Regexp) []int64 { + parts := r.FindStringSubmatch(text) + var result []int64 + for _, p := range parts[1:] { + // It is safe to igore the error here, because at this point we know + // the string matches the regexp, and that means it can't be an + // invalid number. + n, _ := strconv.ParseInt(p, 10, 64) + result = append(result, n) + } + return result +} + +// TimeDelta is the difference between a time and another time. +type TimeDelta struct { + Years int64 + Months int64 + Days int64 + Hours int64 + Minutes int64 + Seconds int64 + Microseconds int64 +} + +// Add returns the given time plus the time delta. +func (td TimeDelta) Add(t time.Time) time.Time { + return td.apply(t, 1) +} + +// Sub returns the given time minus the time delta. +func (td TimeDelta) Sub(t time.Time) time.Time { + return td.apply(t, -1) +} + +const ( + day = 24 * time.Hour + week = 7 * day +) + +func (td TimeDelta) apply(t time.Time, sign int64) time.Time { + y := int64(t.Year()) + mo := int64(t.Month()) + d := t.Day() + h := t.Hour() + min := t.Minute() + s := t.Second() + ns := t.Nanosecond() + + if td.Years != 0 { + y += td.Years * sign + } + + if td.Months != 0 { + m := mo + td.Months*sign + if m < 1 { + mo = 12 + (m % 12) + y += m/12 - 1 + } else if m > 12 { + mo = m % 12 + y += m / 12 + } else { + mo = m + } + + // Due to the operations done before, month may be zero, which means it's + // december. + if mo == 0 { + mo = 12 + } + } + + if days := daysInMonth(time.Month(mo), int(y)); days < d { + d = days + } + + date := time.Date(int(y), time.Month(mo), d, h, min, s, ns, t.Location()) + + if td.Days != 0 { + date = date.Add(time.Duration(td.Days) * day * time.Duration(sign)) + } + + if td.Hours != 0 { + date = date.Add(time.Duration(td.Hours) * time.Hour * time.Duration(sign)) + } + + if td.Minutes != 0 { + date = date.Add(time.Duration(td.Minutes) * time.Minute * time.Duration(sign)) + } + + if td.Seconds != 0 { + date = date.Add(time.Duration(td.Seconds) * time.Second * time.Duration(sign)) + } + + if td.Microseconds != 0 { + date = date.Add(time.Duration(td.Microseconds) * time.Microsecond * time.Duration(sign)) + } + + return date +} + +func daysInMonth(month time.Month, year int) int { + if month == time.December { + return 31 + } + + date := time.Date(year, month+time.Month(1), 1, 0, 0, 0, 0, time.Local) + return date.Add(-1 * day).Day() +} diff --git a/vendor/gopkg.in/src-d/go-mysql-server.v0/sql/parse/parse.go b/vendor/gopkg.in/src-d/go-mysql-server.v0/sql/parse/parse.go index f3a80275c..a222be31c 100644 --- a/vendor/gopkg.in/src-d/go-mysql-server.v0/sql/parse/parse.go +++ b/vendor/gopkg.in/src-d/go-mysql-server.v0/sql/parse/parse.go @@ -826,6 +826,8 @@ func exprToExpression(e sqlparser.Expr) (sql.Expression, error) { return nil, ErrUnsupportedSubqueryExpression.New() case *sqlparser.CaseExpr: return caseExprToExpression(v) + case *sqlparser.IntervalExpr: + return intervalExprToExpression(v) } } @@ -1011,6 +1013,16 @@ func binaryExprToExpression(be *sqlparser.BinaryExpr) (sql.Expression, error) { return nil, err } + _, lok := l.(*expression.Interval) + _, rok := r.(*expression.Interval) + if lok && be.Operator == "-" { + return nil, ErrUnsupportedSyntax.New("subtracting from an interval") + } else if (lok || rok) && be.Operator != "+" && be.Operator != "-" { + return nil, ErrUnsupportedSyntax.New("only + and - can be used to add of subtract intervals from dates") + } else if lok && rok { + return nil, ErrUnsupportedSyntax.New("intervals cannot be added or subtracted from other intervals") + } + return expression.NewArithmetic(l, r, be.Operator), nil default: @@ -1058,6 +1070,15 @@ func caseExprToExpression(e *sqlparser.CaseExpr) (sql.Expression, error) { return expression.NewCase(expr, branches, elseExpr), nil } +func intervalExprToExpression(e *sqlparser.IntervalExpr) (sql.Expression, error) { + expr, err := exprToExpression(e.Expr) + if err != nil { + return nil, err + } + + return expression.NewInterval(expr, e.Unit), nil +} + func removeComments(s string) string { r := bufio.NewReader(strings.NewReader(s)) var result []rune