diff --git a/.pullapprove.yml b/.pullapprove.yml index 06ba352530b..043417ce44a 100644 --- a/.pullapprove.yml +++ b/.pullapprove.yml @@ -28,4 +28,5 @@ groups: - pivanof - pivanof-bot - wangyipei01 + - wangyipei01-bot diff --git a/data/test/tabletserver/exec_cases.txt b/data/test/tabletserver/exec_cases.txt index 74825a55e6b..92965cce76d 100644 --- a/data/test/tabletserver/exec_cases.txt +++ b/data/test/tabletserver/exec_cases.txt @@ -578,7 +578,8 @@ "FullQuery": "update b set eid = 1", "OuterQuery": "update b set eid = 1 where :#pk", "Subquery": "select eid, id from b limit :#maxLimit for update", - "SecondaryPKValues": [1, null] + "SecondaryPKValues": [1, null], + "WhereClause": "" } # type mismatch @@ -591,7 +592,8 @@ "PlanID": "PASS_DML", "Reason": "PK_CHANGE", "TableName": "b", - "FullQuery": "update b set eid = foo()" + "FullQuery": "update b set eid = foo()", + "WhereClause": "" } # update subquery @@ -601,7 +603,8 @@ "TableName": "a", "FullQuery": "update a set name = 'foo'", "OuterQuery": "update a set name = 'foo' where :#pk", - "Subquery": "select eid, id from a limit :#maxLimit for update" + "Subquery": "select eid, id from a limit :#maxLimit for update", + "WhereClause": "" } # update complex where clause @@ -611,7 +614,8 @@ "TableName": "a", "FullQuery": "update a set name = 'foo' where eid + 1 = 1", "OuterQuery": "update a set name = 'foo' where :#pk", - "Subquery": "select eid, id from a where eid + 1 = 1 limit :#maxLimit for update" + "Subquery": "select eid, id from a where eid + 1 = 1 limit :#maxLimit for update", + "WhereClause": " where eid + 1 = 1" } # pk @@ -621,7 +625,8 @@ "TableName": "a", "FullQuery": "update a set name = 'foo' where eid = 1 and id = 1", "OuterQuery": "update a set name = 'foo' where :#pk", - "PKValues": [1, 1] + "PKValues": [1, 1], + "WhereClause": " where eid = 1 and id = 1" } # partial pk @@ -631,7 +636,8 @@ "TableName": "a", "FullQuery": "update a set name = 'foo' where eid = 1", "OuterQuery": "update a set name = 'foo' where :#pk", - "Subquery": "select eid, id from a where eid = 1 limit :#maxLimit for update" + "Subquery": "select eid, id from a where eid = 1 limit :#maxLimit for update", + "WhereClause": " where eid = 1" } # bad pk @@ -641,7 +647,8 @@ "TableName": "a", "FullQuery": "update a set name = 'foo' where eid = 1.0 and id = 1", "OuterQuery": "update a set name = 'foo' where :#pk", - "Subquery": "select eid, id from a where eid = 1.0 and id = 1 limit :#maxLimit for update" + "Subquery": "select eid, id from a where eid = 1.0 and id = 1 limit :#maxLimit for update", + "WhereClause": " where eid = 1.0 and id = 1" } # partial pk with limit @@ -651,7 +658,8 @@ "TableName": "a", "FullQuery": "update a set name = 'foo' where eid = 1 limit 10", "OuterQuery": "update a set name = 'foo' where :#pk", - "Subquery": "select eid, id from a where eid = 1 limit 10 for update" + "Subquery": "select eid, id from a where eid = 1 limit 10 for update", + "WhereClause": " where eid = 1" } # non-pk @@ -661,7 +669,8 @@ "TableName": "a", "FullQuery": "update a set name = 'foo' where eid = 1 and name = 'foo'", "OuterQuery": "update a set name = 'foo' where :#pk", - "Subquery": "select eid, id from a where eid = 1 and name = 'foo' limit :#maxLimit for update" + "Subquery": "select eid, id from a where eid = 1 and name = 'foo' limit :#maxLimit for update", + "WhereClause": " where eid = 1 and name = 'foo'" } # no index @@ -670,7 +679,8 @@ "PlanID": "PASS_DML", "Reason": "TABLE_NOINDEX", "TableName": "c", - "FullQuery": "update c set eid = 1" + "FullQuery": "update c set eid = 1", + "WhereClause": "" } # complex expression in where @@ -680,7 +690,8 @@ "TableName":"a", "FullQuery":"update a set name = 'foo' where eid + 1 = 1 and id = 1", "OuterQuery":"update a set name = 'foo' where :#pk", - "Subquery":"select eid, id from a where eid + 1 = 1 and id = 1 limit :#maxLimit for update" + "Subquery":"select eid, id from a where eid + 1 = 1 and id = 1 limit :#maxLimit for update", + "WhereClause": " where eid + 1 = 1 and id = 1" } # parenthesized expressions in where @@ -690,7 +701,8 @@ "TableName": "a", "FullQuery": "update a set name = 'foo' where (eid = 1) and id = 1", "OuterQuery": "update a set name = 'foo' where :#pk", - "PKValues": [1, 1] + "PKValues": [1, 1], + "WhereClause": " where (eid = 1) and id = 1" } # in clause expression in where @@ -700,7 +712,8 @@ "TableName":"a", "FullQuery":"update a set name = 'foo' where eid in (1, 2) and id = 1", "OuterQuery":"update a set name = 'foo' where :#pk", - "PKValues":[[1,2],1] + "PKValues":[[1,2],1], + "WhereClause": " where eid in (1, 2) and id = 1" } # double in clause @@ -710,7 +723,8 @@ "TableName":"a", "FullQuery":"update a set name = 'foo' where eid in (1, 2) and id in (1, 2)", "OuterQuery":"update a set name = 'foo' where :#pk", - "Subquery":"select eid, id from a where eid in (1, 2) and id in (1, 2) limit :#maxLimit for update" + "Subquery":"select eid, id from a where eid in (1, 2) and id in (1, 2) limit :#maxLimit for update", + "WhereClause": " where eid in (1, 2) and id in (1, 2)" } # double use of pk @@ -720,7 +734,8 @@ "TableName":"a", "FullQuery":"update a set name = 'foo' where eid = 1 and eid = 2", "OuterQuery":"update a set name = 'foo' where :#pk", - "Subquery":"select eid, id from a where eid = 1 and eid = 2 limit :#maxLimit for update" + "Subquery":"select eid, id from a where eid = 1 and eid = 2 limit :#maxLimit for update", + "WhereClause": " where eid = 1 and eid = 2" } # delete cross-db @@ -739,7 +754,8 @@ "TableName": "a", "FullQuery": "delete from a", "OuterQuery": "delete from a where :#pk", - "Subquery": "select eid, id from a limit :#maxLimit for update" + "Subquery": "select eid, id from a limit :#maxLimit for update", + "WhereClause": "" } # delete complex where clause @@ -749,7 +765,8 @@ "TableName": "a", "FullQuery": "delete from a where eid + 1 = 1", "OuterQuery": "delete from a where :#pk", - "Subquery": "select eid, id from a where eid + 1 = 1 limit :#maxLimit for update" + "Subquery": "select eid, id from a where eid + 1 = 1 limit :#maxLimit for update", + "WhereClause": " where eid + 1 = 1" } # pk @@ -759,7 +776,8 @@ "TableName": "a", "FullQuery": "delete from a where eid = 1 and id = 1", "OuterQuery": "delete from a where :#pk", - "PKValues": [1, 1] + "PKValues": [1, 1], + "WhereClause": " where eid = 1 and id = 1" } # partial pk @@ -769,7 +787,8 @@ "TableName": "a", "FullQuery": "delete from a where eid = 1", "OuterQuery": "delete from a where :#pk", - "Subquery": "select eid, id from a where eid = 1 limit :#maxLimit for update" + "Subquery": "select eid, id from a where eid = 1 limit :#maxLimit for update", + "WhereClause": " where eid = 1" } # bad pk value delete @@ -779,7 +798,8 @@ "TableName": "a", "FullQuery": "delete from a where eid = 1.0 and id = 1", "OuterQuery": "delete from a where :#pk", - "Subquery": "select eid, id from a where eid = 1.0 and id = 1 limit :#maxLimit for update" + "Subquery": "select eid, id from a where eid = 1.0 and id = 1 limit :#maxLimit for update", + "WhereClause": " where eid = 1.0 and id = 1" } # non-pk @@ -789,7 +809,8 @@ "TableName": "a", "FullQuery": "delete from a where eid = 1 and name = 'foo'", "OuterQuery": "delete from a where :#pk", - "Subquery": "select eid, id from a where eid = 1 and name = 'foo' limit :#maxLimit for update" + "Subquery": "select eid, id from a where eid = 1 and name = 'foo' limit :#maxLimit for update", + "WhereClause": " where eid = 1 and name = 'foo'" } # no index @@ -798,7 +819,8 @@ "PlanID": "PASS_DML", "Reason": "TABLE_NOINDEX", "TableName": "c", - "FullQuery": "delete from c" + "FullQuery": "delete from c", + "WhereClause": "" } # delete complex expression in where @@ -808,7 +830,8 @@ "TableName":"a", "FullQuery":"delete from a where eid + 1 = 1 and id = 1", "OuterQuery":"delete from a where :#pk", - "Subquery":"select eid, id from a where eid + 1 = 1 and id = 1 limit :#maxLimit for update" + "Subquery":"select eid, id from a where eid + 1 = 1 and id = 1 limit :#maxLimit for update", + "WhereClause": " where eid + 1 = 1 and id = 1" } # parenthesized expressions in where @@ -818,7 +841,8 @@ "TableName": "a", "FullQuery": "delete from a where (eid = 1) and id = 1", "OuterQuery": "delete from a where :#pk", - "PKValues": [1, 1] + "PKValues": [1, 1], + "WhereClause": " where (eid = 1) and id = 1" } # delete in clause expression in where @@ -828,7 +852,8 @@ "TableName":"a", "FullQuery":"delete from a where eid in (1, 2) and id = 1", "OuterQuery":"delete from a where :#pk", - "PKValues":[[1,2],1] + "PKValues":[[1,2],1], + "WhereClause": " where eid in (1, 2) and id = 1" } # delete double in clause @@ -838,7 +863,8 @@ "TableName":"a", "FullQuery":"delete from a where eid in (1, 2) and id in (1, 2)", "OuterQuery":"delete from a where :#pk", - "Subquery":"select eid, id from a where eid in (1, 2) and id in (1, 2) limit :#maxLimit for update" + "Subquery":"select eid, id from a where eid in (1, 2) and id in (1, 2) limit :#maxLimit for update", + "WhereClause": " where eid in (1, 2) and id in (1, 2)" } # delete double use of pk @@ -848,7 +874,8 @@ "TableName":"a", "FullQuery":"delete from a where eid = 1 and eid = 2", "OuterQuery":"delete from a where :#pk", - "Subquery":"select eid, id from a where eid = 1 and eid = 2 limit :#maxLimit for update" + "Subquery":"select eid, id from a where eid = 1 and eid = 2 limit :#maxLimit for update", + "WhereClause": " where eid = 1 and eid = 2" } # single value sequence diff --git a/data/test/vtgate/show_cases.txt b/data/test/vtgate/show_cases.txt new file mode 100644 index 00000000000..83b42696e77 --- /dev/null +++ b/data/test/vtgate/show_cases.txt @@ -0,0 +1,53 @@ +# unsupported show +"show create database" +"unsupported show statement" + +# show databases +"show databases" +{ + "Original": "show databases", + "Instructions": { + "Opcode": "Metadata", + "Query": "show databases" + } +} + +# show tables +"show tables" +{ + "Original": "show tables", + "Instructions": { + "Opcode": "Metadata", + "Query": "show tables" + } +} + +# show vitess_keyspaces +"show vitess_keyspaces" +{ + "Original": "show vitess_keyspaces", + "Instructions": { + "Opcode": "Metadata", + "Query": "show vitess_keyspaces" + } +} + +# show vitess_shards +"show vitess_shards" +{ + "Original": "show vitess_shards", + "Instructions": { + "Opcode": "Metadata", + "Query": "show vitess_shards" + } +} + +# show vschema_tables +"show vschema_tables" +{ + "Original": "show vschema_tables", + "Instructions": { + "Opcode": "Metadata", + "Query": "show vschema_tables" + } +} diff --git a/doc/APIScope.md b/doc/APIScope.md new file mode 100644 index 00000000000..2442ff58e84 --- /dev/null +++ b/doc/APIScope.md @@ -0,0 +1,95 @@ +# API Scope + +This document describes the scope of the Vitess APIs, and how they map to +traditional concepts like database and tables. + +## Introduction + +Vitess is exposed as a single database by clients, but can be composed of any +arbitrary number of databases, some of them possibly sharded with large numbers +of shards. It is not obvious to map this system with clients that expect only a +single database. + +## Shard Names and Key Ranges + +For unsharded keyspaces, or custom sharded keyspaces, the shard names have +traditionally been numbers e.g. `0`. (It is important to note that Vitess *does +not* interpret these shard names, including name `0`, as range-based shard.) + +For keyspaces that are sharded by keyrange, we use the range as the shard +name. For instance `40-80` contains all records whose sharding key is between +0x40..... and 0x80.... + +The conventions Vitess follow is: + +* if a single shard should be targeted, the shard name should be used. This + allows single-shard keyspaces and custom sharded keyspaces to be accessed. + +* if a subset of the data should be targeted, and we are using ranged-based + sharding, then a key range should be used. Vitess is then responsible for + mapping the keyrange to a list of shards, and for any aggregation. + +## Execute API + +The main entry point of a Vitess cluster is the 'Execute' call (or StreamExecute +for streaming queries). It takes a keyspace, a tablet type, and a query. The +VSchema helps Vitess route the query to the right shard and tablet type. This is +the most transparent way of accessing Vitess. Keyspace is the database, and +inside the query, a table is referenced either just by name, or by +`keyspace.name`. + +We are adding a `shard` parameter to this entry point. It will work as follows: + +* TODO(sougou) document this. + +We want to add support for DBA statements: + +* DDL statements in the short term will be sent to all shards of a + keyspace. Note for complex schema changes, using Schema Swap is recommended. + Longer term, we want to instead trigger a workflow that will use the best + strategy for the schema change. + +* Read-only statements (like `describe table`) will be sent to the first shard + of the keyspace. This somewhat assumes the schema is consistent across all + shards, which may or may not be true. Guaranteeing schema consistency across + shards is however out of scope for this. + +* Read-only statements that return table statistics (like data size, number of + rows, ...) will need to be scattered and aggregated across all shards. The + first version of the API won't support this. + +We also want to add support for routing sequence queries through this Execute +query (right now only the VSchema engine can use a sequence). + +Then, we also want to support changing the VSchema via SQL-like statements, like +`ALTER TABLE ADD VINDEX()`. + +## Client Connectors + +Client connectors (like JDBC) usually have a connection string that describes +the connection. It usually includes the keyspace and the tablet type, and uses +the Execute API. + +## MySQL Server Protocol API + +vtgate now exposes an API endpoint that implements the regular MySQL server +protocol. All calls are forwarded to the Execute API. The database provided on +the connection is used as the keyspace, if any. The tablet type is also Master +for now. + +## Update Stream and Message Stream + +These APIs are meant to target a keyspace and optionally a subset of its shards. + +* The keyspace is provided in the API. + +* To specify the shard, two options are provided: + + * a shard name, to target an individual shard by name. + + * a key range, to target a subset of shards in a way that will survive a + resharding event. + +Note the current implementation for these services only supports a key range +that exactly maps to one shard. We want to make that better and support +aggregating multiple shards in a keyrange. diff --git a/doc/RowBasedReplication.md b/doc/RowBasedReplication.md index c4d00111af8..76e19f07130 100644 --- a/doc/RowBasedReplication.md +++ b/doc/RowBasedReplication.md @@ -31,27 +31,31 @@ A few binlog events are used: The [`binlog-row-image` option](https://dev.mysql.com/doc/refman/5.7/en/replication-options-binary-log.html#sysvar_binlog_row_image) can be used to control which rows are used to identify the columns for the Update -and Delete Rows events. The default setting for that option is to log all columns. +and Delete Rows events. The default setting for that option is to log all +columns. -## Vitess Use of Replication Stream +## Vitess Use of MySQL Replication Stream Vitess uses the Replication Stream in a number of places. This part explains how -we can use RBR for these. +we use RBR for these. ### vttablet Replication Stream Watcher -This is enabled by the `watch_replication_stream` option, and is used by [Update -Stream](/user-guide/update-stream.html). It only cares about the GTIDs for the events, so it is unaffected by the use of RBR. +This is enabled by the `watch_replication_stream` option, and is used +by [Update Stream](/user-guide/update-stream.html). It only cares about the +GTIDs for the events, so it is unaffected by the use of RBR. *Note*: the current vttablet also reloads the schema when it sees a DDL in the -stream. See below for more information on this. +stream. See below for more information on this. DDLs are however not represented +in RBR, so this is an orthogonal issue. ### Update Stream The current implementation uses comments in the original SQL (in SQR) to provide the primary key of the column that is being changed. -We are changing this to also parse the RBR events, and extract the primary key value. +We are changing this to also parse the RBR events, and extract the primary key +value. *Note*: this means we need accurate schema information. See below. @@ -116,6 +120,28 @@ We have future plans to: * Maintain a history of the schema changes that happen on all shards, so events can be parsed correctly in all cases. +## Unsupported Features + +This part describes the features that are not supported for RBR in Vitess as of +March 2017: + +* *Fractional timestamps for MariaDB*: not supported. This affects the objects + of type `TIMESTAMP`, `TIME` and `DATETIME`. The way that feature is + implemented in MariaDB, the binary logs do not contain enough information to + be parsed, but instead MariaDB relies on the schema knowledge. This is very + fragile. MySQL 5.6+ added new data types, and these are supported. + +* *JSON type in MySQL 5.7+*: the representation of these in the binlogs is a + blob containing indexed binary data. Re-building the SQL version of the data, + so it can be re-inserted during resharding, is not supported yet. It wouldn't + however be a lot of work, with other libraries also supporting this, and the + C++ MySQL code being well written and easy to read. See for instance + https://github.com/shyiko/mysql-binlog-connector-java/pull/119 + +* *Timezones support*: the binary logs store timestamps in UTC. When converting + these to SQL, we print the UTC value. If the server is not in UTC, that will + result in data corruption. *Note*: we are working on a fix for that one. + ## Update Stream Extensions [Update Stream](/user-guide/update-stream.html) can be changed to contain both @@ -138,5 +164,5 @@ A lot of the work done by vttablet now is to find the Primary Key of the modified rows, to rewrite the queries in an efficient way and tag each statement with the Primary Key. None of this may be necessary with RBR. -We plan to eventually add a `rbr_mode` flag to vttablet to disable all the things it can -skip if RBR is used. +We plan to eventually add a `rbr_mode` flag to vttablet to disable all the +things it can skip if RBR is used. diff --git a/doc/SeparatingVttabletMysql.md b/doc/SeparatingVttabletMysql.md new file mode 100644 index 00000000000..f5c3ddca8d1 --- /dev/null +++ b/doc/SeparatingVttabletMysql.md @@ -0,0 +1,84 @@ +# Separating VTTablet and MySQL + +This document explores the setup where VTTablet and MySQL are not running on the +same server, or in the same container. This is mostly useful when using a +managed MySQL server (like CloudSQL or RDS). + +In this setup, Vitess is not responsible for any master management, or +backups. Schema Swap is also not possible, as it relies on backups / restores. + +`Note`: this document is a work in progress, meant to centralize all findings on +this subject. Eventually, we want to have an end to end test proving this setup +works, probably on Kubernetes / GCE using CloudSQL. + +## VTTablet Configuration + +The following adjustments need to be made to VTTablet command line parameters: + +* Do not specify `-mycnf-file`: instead, specify the `-mycnf_server_id` + parameter. + +* Do not use `-mycnf_socket_file`. There is no local MySQL unix socket file. + +* Specify the host and port of the MySQL daemon for the `-db-config-XXX-host` + and `-db-config-XXX-port` command line parameters. Do not specify + `-db-config-XXX-unixsocket` parameters. + +* Disable restores / backups, by not passing any backup command line + parameters. Specifically, `-restore_from_backup` and + `-backup_storage_implementation` shoud not be set. + +Since master management and replication are not handled by Vitess, we just need +to make sure the tablet type in the topology is correct before running +vttablet. Usually, vttablet can be started with `-init_tablet_type replica`, +even for a master (as `master` is not allowed), and will figure out the master +and set its type to `master`. When Vitess doesn't manage that at all, running +`vtctl InitShardMaster` is not possible, so there is no way to start as the +master just using vttablet. There are two solutions: + +* Preferred: Start the master with `-init_tablet_type replica`, and then run a + `vtctl TabletExternallyReparented ` for the actual master. + +* Run `vtctl InitTablet ... master` for the master, and then run vttablet with + no `-init...` parameters. + +## Other Configurations + +`vtctl` and `vtctld` can be run with the `-disable_active_reparents` flag. This +would make all explicit reparent commands unreachable (like `InitShardMaster` +or `PlannedReparentShard`). + +`vtgate` and `vtworker` don't need any special configuration. + +## Runtime Differences + +There are some subtle differences when connecting to MySQL using TCP, as opposed +to connecting using a Unix socket. The main one is that when the server closes +the connection, the client knows it when writing on a unix socket, but only +realizes it when reading the response from a TCP socket. So using TCP, it is +harder to distinguish a query that was killed from a query that was sent on a +closed connection. It matters because we don't want to retry a query that was +killed, but we want to retry a query sent on a closed connection. + +If this becomes an issue, we can find ways around it. + +## Resharding + +Given that Vitess doesn't control master management or replication, is +resharding still possible? The following features are necessary: + +* being able to start / stop replication on a slave. So we can keep a slave in a + specific state while we copy or compare data. + +* have access to the current replication position, as MariaDB GTID or MySQL 5.6 + GTIDSet. + +* be able to connect as a replication slave to MySQL, and start from an + arbitrary past GTID, to support filtered replication. + +`Note`: in some setups, the master for a shard is never moved around, and is +made durable by a persistent drive. It would be possible in that case to have an +alternative implementation of the Vitess GTID code using a filename / file +position. Filtered replication would always need to connect to the master of the +source shard then. Let us know if you are in that situation, it is not a lot of +work to implement. diff --git a/examples/demo/run.py b/examples/demo/run.py index 7339f8c3f89..fcc6f2e5b57 100755 --- a/examples/demo/run.py +++ b/examples/demo/run.py @@ -51,7 +51,7 @@ def start_vitess(): sp = subprocess.Popen(args, stdin=subprocess.PIPE, stdout=subprocess.PIPE) # This load will make us wait for vitess to come up. - json.loads(sp.stdout.readline()) + print json.loads(sp.stdout.readline()) return sp diff --git a/go/cmd/vtgateclienttest/services/callerid.go b/go/cmd/vtgateclienttest/services/callerid.go index 090bdf952cc..a4e9e759546 100644 --- a/go/cmd/vtgateclienttest/services/callerid.go +++ b/go/cmd/vtgateclienttest/services/callerid.go @@ -65,9 +65,9 @@ func (c *callerIDClient) checkCallerID(ctx context.Context, received string) (bo return true, fmt.Errorf("SUCCESS: callerid matches") } -func (c *callerIDClient) Execute(ctx context.Context, sql string, bindVariables map[string]interface{}, keyspace string, tabletType topodatapb.TabletType, session *vtgatepb.Session, notInTransaction bool, options *querypb.ExecuteOptions) (*sqltypes.Result, error) { +func (c *callerIDClient) Execute(ctx context.Context, sql string, bindVariables map[string]interface{}, keyspace string, tabletType topodatapb.TabletType, session *vtgatepb.Session, notInTransaction bool, options *querypb.ExecuteOptions) (*vtgatepb.Session, *sqltypes.Result, error) { if ok, err := c.checkCallerID(ctx, sql); ok { - return nil, err + return session, nil, err } return c.fallbackClient.Execute(ctx, sql, bindVariables, keyspace, tabletType, session, notInTransaction, options) } @@ -100,13 +100,13 @@ func (c *callerIDClient) ExecuteEntityIds(ctx context.Context, sql string, bindV return c.fallbackClient.ExecuteEntityIds(ctx, sql, bindVariables, keyspace, entityColumnName, entityKeyspaceIDs, tabletType, session, notInTransaction, options) } -func (c *callerIDClient) ExecuteBatch(ctx context.Context, sqlList []string, bindVariablesList []map[string]interface{}, keyspace string, tabletType topodatapb.TabletType, asTransaction bool, session *vtgatepb.Session, options *querypb.ExecuteOptions) ([]sqltypes.QueryResponse, error) { +func (c *callerIDClient) ExecuteBatch(ctx context.Context, sqlList []string, bindVariablesList []map[string]interface{}, keyspace string, tabletType topodatapb.TabletType, session *vtgatepb.Session, options *querypb.ExecuteOptions) (*vtgatepb.Session, []sqltypes.QueryResponse, error) { if len(sqlList) == 1 { if ok, err := c.checkCallerID(ctx, sqlList[0]); ok { - return nil, err + return session, nil, err } } - return c.fallbackClient.ExecuteBatch(ctx, sqlList, bindVariablesList, keyspace, tabletType, asTransaction, session, options) + return c.fallbackClient.ExecuteBatch(ctx, sqlList, bindVariablesList, keyspace, tabletType, session, options) } func (c *callerIDClient) ExecuteBatchShards(ctx context.Context, queries []*vtgatepb.BoundShardQuery, tabletType topodatapb.TabletType, asTransaction bool, session *vtgatepb.Session, options *querypb.ExecuteOptions) ([]sqltypes.Result, error) { diff --git a/go/cmd/vtgateclienttest/services/echo.go b/go/cmd/vtgateclienttest/services/echo.go index f799e3a7bf0..6958331d5aa 100644 --- a/go/cmd/vtgateclienttest/services/echo.go +++ b/go/cmd/vtgateclienttest/services/echo.go @@ -96,9 +96,9 @@ func echoQueryResult(vals map[string]interface{}) *sqltypes.Result { return qr } -func (c *echoClient) Execute(ctx context.Context, sql string, bindVariables map[string]interface{}, keyspace string, tabletType topodatapb.TabletType, session *vtgatepb.Session, notInTransaction bool, options *querypb.ExecuteOptions) (*sqltypes.Result, error) { +func (c *echoClient) Execute(ctx context.Context, sql string, bindVariables map[string]interface{}, keyspace string, tabletType topodatapb.TabletType, session *vtgatepb.Session, notInTransaction bool, options *querypb.ExecuteOptions) (*vtgatepb.Session, *sqltypes.Result, error) { if strings.HasPrefix(sql, EchoPrefix) { - return echoQueryResult(map[string]interface{}{ + return session, echoQueryResult(map[string]interface{}{ "callerId": callerid.EffectiveCallerIDFromContext(ctx), "query": sql, "bindVars": bindVariables, @@ -181,7 +181,7 @@ func (c *echoClient) ExecuteEntityIds(ctx context.Context, sql string, bindVaria return c.fallbackClient.ExecuteEntityIds(ctx, sql, bindVariables, keyspace, entityColumnName, entityKeyspaceIDs, tabletType, session, notInTransaction, options) } -func (c *echoClient) ExecuteBatch(ctx context.Context, sqlList []string, bindVariablesList []map[string]interface{}, keyspace string, tabletType topodatapb.TabletType, asTransaction bool, session *vtgatepb.Session, options *querypb.ExecuteOptions) ([]sqltypes.QueryResponse, error) { +func (c *echoClient) ExecuteBatch(ctx context.Context, sqlList []string, bindVariablesList []map[string]interface{}, keyspace string, tabletType topodatapb.TabletType, session *vtgatepb.Session, options *querypb.ExecuteOptions) (*vtgatepb.Session, []sqltypes.QueryResponse, error) { if len(sqlList) > 0 && strings.HasPrefix(sqlList[0], EchoPrefix) { var queryResponse []sqltypes.QueryResponse if bindVariablesList == nil { @@ -189,20 +189,19 @@ func (c *echoClient) ExecuteBatch(ctx context.Context, sqlList []string, bindVar } for queryNum, query := range sqlList { result := echoQueryResult(map[string]interface{}{ - "callerId": callerid.EffectiveCallerIDFromContext(ctx), - "query": query, - "bindVars": bindVariablesList[queryNum], - "keyspace": keyspace, - "tabletType": tabletType, - "session": session, - "asTransaction": asTransaction, - "options": options, + "callerId": callerid.EffectiveCallerIDFromContext(ctx), + "query": query, + "bindVars": bindVariablesList[queryNum], + "keyspace": keyspace, + "tabletType": tabletType, + "session": session, + "options": options, }) queryResponse = append(queryResponse, sqltypes.QueryResponse{QueryResult: result, QueryError: nil}) } - return queryResponse, nil + return session, queryResponse, nil } - return c.fallbackClient.ExecuteBatch(ctx, sqlList, bindVariablesList, keyspace, tabletType, asTransaction, session, options) + return c.fallbackClient.ExecuteBatch(ctx, sqlList, bindVariablesList, keyspace, tabletType, session, options) } func (c *echoClient) ExecuteBatchShards(ctx context.Context, queries []*vtgatepb.BoundShardQuery, tabletType topodatapb.TabletType, asTransaction bool, session *vtgatepb.Session, options *querypb.ExecuteOptions) ([]sqltypes.Result, error) { diff --git a/go/cmd/vtgateclienttest/services/errors.go b/go/cmd/vtgateclienttest/services/errors.go index fbe5d2eb7f2..110c79ca2fe 100644 --- a/go/cmd/vtgateclienttest/services/errors.go +++ b/go/cmd/vtgateclienttest/services/errors.go @@ -101,12 +101,12 @@ func trimmedRequestToError(received string) error { } } -func (c *errorClient) Execute(ctx context.Context, sql string, bindVariables map[string]interface{}, keyspace string, tabletType topodatapb.TabletType, session *vtgatepb.Session, notInTransaction bool, options *querypb.ExecuteOptions) (*sqltypes.Result, error) { +func (c *errorClient) Execute(ctx context.Context, sql string, bindVariables map[string]interface{}, keyspace string, tabletType topodatapb.TabletType, session *vtgatepb.Session, notInTransaction bool, options *querypb.ExecuteOptions) (*vtgatepb.Session, *sqltypes.Result, error) { if err := requestToPartialError(sql, session); err != nil { - return nil, err + return session, nil, err } if err := requestToError(sql); err != nil { - return nil, err + return session, nil, err } return c.fallbackClient.Execute(ctx, sql, bindVariables, keyspace, tabletType, session, notInTransaction, options) } @@ -151,16 +151,16 @@ func (c *errorClient) ExecuteEntityIds(ctx context.Context, sql string, bindVari return c.fallbackClient.ExecuteEntityIds(ctx, sql, bindVariables, keyspace, entityColumnName, entityKeyspaceIDs, tabletType, session, notInTransaction, options) } -func (c *errorClient) ExecuteBatch(ctx context.Context, sqlList []string, bindVariablesList []map[string]interface{}, keyspace string, tabletType topodatapb.TabletType, asTransaction bool, session *vtgatepb.Session, options *querypb.ExecuteOptions) ([]sqltypes.QueryResponse, error) { +func (c *errorClient) ExecuteBatch(ctx context.Context, sqlList []string, bindVariablesList []map[string]interface{}, keyspace string, tabletType topodatapb.TabletType, session *vtgatepb.Session, options *querypb.ExecuteOptions) (*vtgatepb.Session, []sqltypes.QueryResponse, error) { if len(sqlList) == 1 { if err := requestToPartialError(sqlList[0], session); err != nil { - return nil, err + return session, nil, err } if err := requestToError(sqlList[0]); err != nil { - return nil, err + return session, nil, err } } - return c.fallbackClient.ExecuteBatch(ctx, sqlList, bindVariablesList, keyspace, tabletType, asTransaction, session, options) + return c.fallbackClient.ExecuteBatch(ctx, sqlList, bindVariablesList, keyspace, tabletType, session, options) } func (c *errorClient) ExecuteBatchShards(ctx context.Context, queries []*vtgatepb.BoundShardQuery, tabletType topodatapb.TabletType, asTransaction bool, session *vtgatepb.Session, options *querypb.ExecuteOptions) ([]sqltypes.Result, error) { diff --git a/go/cmd/vtgateclienttest/services/fallback.go b/go/cmd/vtgateclienttest/services/fallback.go index 705064bdd7a..da4779502c0 100644 --- a/go/cmd/vtgateclienttest/services/fallback.go +++ b/go/cmd/vtgateclienttest/services/fallback.go @@ -27,7 +27,7 @@ func newFallbackClient(fallback vtgateservice.VTGateService) fallbackClient { return fallbackClient{fallback: fallback} } -func (c fallbackClient) Execute(ctx context.Context, sql string, bindVariables map[string]interface{}, keyspace string, tabletType topodatapb.TabletType, session *vtgatepb.Session, notInTransaction bool, options *querypb.ExecuteOptions) (*sqltypes.Result, error) { +func (c fallbackClient) Execute(ctx context.Context, sql string, bindVariables map[string]interface{}, keyspace string, tabletType topodatapb.TabletType, session *vtgatepb.Session, notInTransaction bool, options *querypb.ExecuteOptions) (*vtgatepb.Session, *sqltypes.Result, error) { return c.fallback.Execute(ctx, sql, bindVariables, keyspace, tabletType, session, notInTransaction, options) } @@ -47,8 +47,8 @@ func (c fallbackClient) ExecuteEntityIds(ctx context.Context, sql string, bindVa return c.fallback.ExecuteEntityIds(ctx, sql, bindVariables, keyspace, entityColumnName, entityKeyspaceIDs, tabletType, session, notInTransaction, options) } -func (c fallbackClient) ExecuteBatch(ctx context.Context, sqlList []string, bindVariablesList []map[string]interface{}, keyspace string, tabletType topodatapb.TabletType, asTransaction bool, session *vtgatepb.Session, options *querypb.ExecuteOptions) ([]sqltypes.QueryResponse, error) { - return c.fallback.ExecuteBatch(ctx, sqlList, bindVariablesList, keyspace, tabletType, asTransaction, session, options) +func (c fallbackClient) ExecuteBatch(ctx context.Context, sqlList []string, bindVariablesList []map[string]interface{}, keyspace string, tabletType topodatapb.TabletType, session *vtgatepb.Session, options *querypb.ExecuteOptions) (*vtgatepb.Session, []sqltypes.QueryResponse, error) { + return c.fallback.ExecuteBatch(ctx, sqlList, bindVariablesList, keyspace, tabletType, session, options) } func (c fallbackClient) ExecuteBatchShards(ctx context.Context, queries []*vtgatepb.BoundShardQuery, tabletType topodatapb.TabletType, asTransaction bool, session *vtgatepb.Session, options *querypb.ExecuteOptions) ([]sqltypes.Result, error) { diff --git a/go/cmd/vtgateclienttest/services/terminal.go b/go/cmd/vtgateclienttest/services/terminal.go index c32d484fd31..0f4839ee2e8 100644 --- a/go/cmd/vtgateclienttest/services/terminal.go +++ b/go/cmd/vtgateclienttest/services/terminal.go @@ -29,11 +29,11 @@ func newTerminalClient() *terminalClient { return &terminalClient{} } -func (c *terminalClient) Execute(ctx context.Context, sql string, bindVariables map[string]interface{}, keyspace string, tabletType topodatapb.TabletType, session *vtgatepb.Session, notInTransaction bool, options *querypb.ExecuteOptions) (*sqltypes.Result, error) { +func (c *terminalClient) Execute(ctx context.Context, sql string, bindVariables map[string]interface{}, keyspace string, tabletType topodatapb.TabletType, session *vtgatepb.Session, notInTransaction bool, options *querypb.ExecuteOptions) (*vtgatepb.Session, *sqltypes.Result, error) { if sql == "quit://" { log.Fatal("Received quit:// query. Going down.") } - return nil, errTerminal + return session, nil, errTerminal } func (c *terminalClient) ExecuteShards(ctx context.Context, sql string, bindVariables map[string]interface{}, keyspace string, shards []string, tabletType topodatapb.TabletType, session *vtgatepb.Session, notInTransaction bool, options *querypb.ExecuteOptions) (*sqltypes.Result, error) { @@ -52,13 +52,13 @@ func (c *terminalClient) ExecuteEntityIds(ctx context.Context, sql string, bindV return nil, errTerminal } -func (c *terminalClient) ExecuteBatch(ctx context.Context, sqlList []string, bindVariablesList []map[string]interface{}, keyspace string, tabletType topodatapb.TabletType, asTransaction bool, session *vtgatepb.Session, options *querypb.ExecuteOptions) ([]sqltypes.QueryResponse, error) { +func (c *terminalClient) ExecuteBatch(ctx context.Context, sqlList []string, bindVariablesList []map[string]interface{}, keyspace string, tabletType topodatapb.TabletType, session *vtgatepb.Session, options *querypb.ExecuteOptions) (*vtgatepb.Session, []sqltypes.QueryResponse, error) { if len(sqlList) == 1 { if sqlList[0] == "quit://" { log.Fatal("Received quit:// query. Going down.") } } - return nil, errTerminal + return session, nil, errTerminal } func (c *terminalClient) ExecuteBatchShards(ctx context.Context, queries []*vtgatepb.BoundShardQuery, tabletType topodatapb.TabletType, asTransaction bool, session *vtgatepb.Session, options *querypb.ExecuteOptions) ([]sqltypes.Result, error) { diff --git a/go/cmd/vttablet/vttablet.go b/go/cmd/vttablet/vttablet.go index 864f29f379f..026add03732 100644 --- a/go/cmd/vttablet/vttablet.go +++ b/go/cmd/vttablet/vttablet.go @@ -9,7 +9,6 @@ import ( "flag" log "github.com/golang/glog" - "github.com/youtube/vitess/go/exit" "github.com/youtube/vitess/go/vt/dbconfigs" "github.com/youtube/vitess/go/vt/mysqlctl" "github.com/youtube/vitess/go/vt/servenv" @@ -36,36 +35,34 @@ func init() { } func main() { - defer exit.Recover() - dbconfigFlags := dbconfigs.AppConfig | dbconfigs.AllPrivsConfig | dbconfigs.DbaConfig | dbconfigs.FilteredConfig | dbconfigs.ReplConfig dbconfigs.RegisterFlags(dbconfigFlags) mysqlctl.RegisterFlags() flag.Parse() - tabletenv.Init() if len(flag.Args()) > 0 { flag.Usage() - log.Errorf("vttablet doesn't take any positional arguments") - exit.Return(1) + log.Exit("vttablet doesn't take any positional arguments") + } + if err := tabletenv.VerifyConfig(); err != nil { + log.Exitf("invalid config: %v", err) } + tabletenv.Init() + servenv.Init() if *tabletPath == "" { - log.Errorf("tabletPath required") - exit.Return(1) + log.Exit("tabletPath required") } tabletAlias, err := topoproto.ParseTabletAlias(*tabletPath) if err != nil { - log.Error(err) - exit.Return(1) + log.Exitf("failed to parse -tablet-path: %v", err) } mycnf, err := mysqlctl.NewMycnfFromFlags(tabletAlias.Uid) if err != nil { - log.Errorf("mycnf read failed: %v", err) - exit.Return(1) + log.Exitf("mycnf read failed: %v", err) } dbcfgs, err := dbconfigs.Init(mycnf.SocketFile, dbconfigFlags) @@ -90,8 +87,7 @@ func main() { // To override default simpleacl, other ACL plugins must set themselves to be default ACL factory tableacl.Register("simpleacl", &simpleacl.Factory{}) } else if *enforceTableACLConfig { - log.Error("table acl config has to be specified with table-acl-config flag because enforce-tableacl-config is set.") - exit.Return(1) + log.Exit("table acl config has to be specified with table-acl-config flag because enforce-tableacl-config is set.") } // tabletacl.Init loads ACL from file if *tableACLConfig is not empty err = tableacl.Init( @@ -103,8 +99,7 @@ func main() { if err != nil { log.Errorf("Fail to initialize Table ACL: %v", err) if *enforceTableACLConfig { - log.Error("Need a valid initial Table ACL when enforce-tableacl-config is set, exiting.") - exit.Return(1) + log.Exit("Need a valid initial Table ACL when enforce-tableacl-config is set, exiting.") } } @@ -121,8 +116,7 @@ func main() { } agent, err = tabletmanager.NewActionAgent(context.Background(), ts, mysqld, qsc, tabletAlias, *dbcfgs, mycnf, int32(*servenv.Port), gRPCPort) if err != nil { - log.Error(err) - exit.Return(1) + log.Exitf("NewActionAgent() failed: %v", err) } servenv.OnClose(func() { diff --git a/go/mysqlconn/auth_server_none.go b/go/mysqlconn/auth_server_none.go new file mode 100644 index 00000000000..5532fab1a13 --- /dev/null +++ b/go/mysqlconn/auth_server_none.go @@ -0,0 +1,28 @@ +package mysqlconn + +// authServerNone accepts any username/password as valid. +// It's meant to be used for testing and prototyping. +// With this config, you can connect to a local vtgate using +// the following command line: 'mysql -P port -h ::'. +type authServerNone struct { +} + +func (a *authServerNone) UseClearText() bool { + return false +} + +func (a *authServerNone) Salt() ([]byte, error) { + return make([]byte, 20), nil +} + +func (a *authServerNone) ValidateHash(salt []byte, user string, authResponse []byte) (string, error) { + return "", nil +} + +func (a *authServerNone) ValidateClearText(user, password string) (string, error) { + panic("unimplemented") +} + +func init() { + RegisterAuthServerImpl("none", &authServerNone{}) +} diff --git a/go/mysqlconn/client_test.go b/go/mysqlconn/client_test.go index f1b3ec259fb..166ee3b3002 100644 --- a/go/mysqlconn/client_test.go +++ b/go/mysqlconn/client_test.go @@ -152,6 +152,34 @@ func testKillWithRealDatabase(t *testing.T, params *sqldb.ConnParams) { assertSQLError(t, err, CRServerLost, SSUnknownSQLState, "EOF") } +// testKill2006WithRealDatabase opens a connection, kills the +// connection from the server side, then waits a bit, and tries to +// execute a command. We make sure we get the right error code. +func testKill2006WithRealDatabase(t *testing.T, params *sqldb.ConnParams) { + ctx := context.Background() + conn, err := Connect(ctx, params) + if err != nil { + t.Fatal(err) + } + + // Kill the connection from the server side. + killConn, err := Connect(ctx, params) + if err != nil { + t.Fatal(err) + } + defer killConn.Close() + + if _, err := killConn.ExecuteFetch(fmt.Sprintf("kill %v", conn.ConnectionID), 1000, false); err != nil { + t.Fatalf("Kill(%v) failed: %v", conn.ConnectionID, err) + } + + // Now we should get a CRServerGone. Since we are using a + // unix socket, we will get a broken pipe when the server + // closes the connection and we are trying to write the command. + _, err = conn.ExecuteFetch("select sleep(10) from dual", 1000, false) + assertSQLError(t, err, CRServerGone, SSUnknownSQLState, "broken pipe") +} + // testDupEntryWithRealDatabase tests a duplicate key is properly raised. func testDupEntryWithRealDatabase(t *testing.T, params *sqldb.ConnParams) { ctx := context.Background() @@ -244,11 +272,17 @@ ssl-key=%v/server-key.pem t.Error(err) } - // Kill tests the query part of the API. + // Kill tests killing a running query returns the right error. t.Run("Kill", func(t *testing.T) { testKillWithRealDatabase(t, ¶ms) }) + // Kill2006 tests killing a connection with no running query + // returns the right error. + t.Run("Kill2006", func(t *testing.T) { + testKill2006WithRealDatabase(t, ¶ms) + }) + // DupEntry tests a duplicate key returns the right error. t.Run("DupEntry", func(t *testing.T) { testDupEntryWithRealDatabase(t, ¶ms) @@ -274,6 +308,11 @@ ssl-key=%v/server-key.pem testRowReplicationWithRealDatabase(t, ¶ms) }) + // Test RBR types are working properly. + t.Run("RBRTypes", func(t *testing.T) { + testRowReplicationTypesWithRealDatabase(t, ¶ms) + }) + // Test Schema queries work as intended. t.Run("Schema", func(t *testing.T) { testSchema(t, ¶ms) diff --git a/go/mysqlconn/doc.go b/go/mysqlconn/doc.go index 236783ffbc9..5720feb29c1 100644 --- a/go/mysqlconn/doc.go +++ b/go/mysqlconn/doc.go @@ -117,4 +117,23 @@ But eventually, we probably want to remove it entirely, as it is not transmitted over the wire. For now, we keep it for backward compatibility with the C client. +-- +Row-based replication: + +The following types or constructs are not yet supported by our RBR: + +- in MariaDB, the type TIMESTAMP(N) where N>0 is stored in the row the + exact same way as TIMESTAMP(0). So there is no way to get N, except + by knowing the table exact schema. This is such a corner case. MySQL + 5.6+ uses TIMESTAMP2, and uses metadata to know the precision, so it + works there very nicely. + + From mariaDB source code comment: + 'So row-based replication between temporal data types of + different precision is not possible in MariaDB.' + +- JSON is stored as an optimized index data blob in the row. We don't + parse it to re-print a text version for re-insertion. Instead, we + just return NULL. So JSOn is not supported. + */ diff --git a/go/mysqlconn/fakesqldb/server.go b/go/mysqlconn/fakesqldb/server.go index b2c4bc12530..80bd93c8b52 100644 --- a/go/mysqlconn/fakesqldb/server.go +++ b/go/mysqlconn/fakesqldb/server.go @@ -3,7 +3,9 @@ package fakesqldb import ( "fmt" - "net" + "io/ioutil" + "os" + "path" "regexp" "strings" "sync" @@ -16,7 +18,12 @@ import ( ) // DB is a fake database and all its methods are thread safe. It -// creates a mysqlconn.Listener and implements the mysqlconn.Handler interface. +// creates a mysqlconn.Listener and implements the mysqlconn.Handler +// interface. We use a Unix socket to connect to the database, as +// this is the most common way for clients to connect to MySQL. This +// impacts the error codes we're getting back: when the server side is +// closed, the client queries will return CRServerGone(2006) when sending +// the data, as opposed to CRServerLost(2013) when reading the response. type DB struct { // Fields set at construction time. @@ -26,6 +33,9 @@ type DB struct { // listener is our mysqlconn.Listener. listener *mysqlconn.Listener + // socketFile is the path to the unix socket file. + socketFile string + // acceptWG is set when we listen, and can be waited on to // make sure we don't accept any more. acceptWG sync.WaitGroup @@ -44,7 +54,7 @@ type DB struct { // errno 2013 ("server lost"). shouldClose bool // data maps tolower(query) to a result. - data map[string]*sqltypes.Result + data map[string]*ExpectedResult // rejectedData maps tolower(query) to an error. rejectedData map[string]error // patternData is a list of regexp to results. @@ -56,6 +66,13 @@ type DB struct { connections map[uint32]*mysqlconn.Conn } +// ExpectedResult holds the data for a matched query. +type ExpectedResult struct { + *sqltypes.Result + // BeforeFunc() is synchronously called before the server returns the result. + BeforeFunc func() +} + type exprResult struct { expr *regexp.Regexp result *sqltypes.Result @@ -63,11 +80,19 @@ type exprResult struct { // New creates a server, and starts listening. func New(t *testing.T) *DB { + // Pick a path for our socket. + socketDir, err := ioutil.TempDir("", "fakesqldb") + if err != nil { + t.Fatalf("ioutil.TempDir failed: %v", err) + } + socketFile := path.Join(socketDir, "fakesqldb.sock") + // Create our DB. db := &DB{ t: t, + socketFile: socketFile, name: "fakesqldb", - data: make(map[string]*sqltypes.Result), + data: make(map[string]*ExpectedResult), rejectedData: make(map[string]error), queryCalled: make(map[string]int), connections: make(map[uint32]*mysqlconn.Conn), @@ -79,8 +104,7 @@ func New(t *testing.T) *DB { } // Start listening. - var err error - db.listener, err = mysqlconn.NewListener("tcp", ":0", authServer, db) + db.listener, err = mysqlconn.NewListener("unix", socketFile, authServer, db) if err != nil { t.Fatalf("NewListener failed: %v", err) } @@ -91,7 +115,7 @@ func New(t *testing.T) *DB { db.listener.Accept() }() - // Return the db and connection parameters. + // Return the db. return db } @@ -105,11 +129,15 @@ func (db *DB) SetName(name string) *DB { } // Close closes the Listener and waits for it to stop accepting. +// It then closes all connections, and cleans up the temporary directory. func (db *DB) Close() { db.listener.Close() db.acceptWG.Wait() db.CloseAllConnections() + + tmpDir := path.Dir(db.socketFile) + os.RemoveAll(tmpDir) } // CloseAllConnections can be used to provoke MySQL client errors for open @@ -156,24 +184,13 @@ func (db *DB) WaitForClose(timeout time.Duration) error { } } -// Host returns the host we're listening on. -func (db *DB) Host() string { - return db.listener.Addr().(*net.TCPAddr).IP.String() -} - -// Port returns the port we're listening on. -func (db *DB) Port() int { - return db.listener.Addr().(*net.TCPAddr).Port -} - // ConnParams returns the ConnParams to connect to the DB. func (db *DB) ConnParams() *sqldb.ConnParams { return &sqldb.ConnParams{ - Host: db.Host(), - Port: db.Port(), - Uname: "user1", - Pass: "password1", - Charset: "utf8", + UnixSocket: db.socketFile, + Uname: "user1", + Pass: "password1", + Charset: "utf8", } } @@ -241,7 +258,10 @@ func (db *DB) ComQuery(c *mysqlconn.Conn, query string) (*sqltypes.Result, error // Check explicit queries from AddQuery(). result, ok := db.data[key] if ok { - return result, nil + if f := result.BeforeFunc; f != nil { + f() + } + return result.Result, nil } // Check query patterns from AddQueryPattern(). @@ -252,7 +272,7 @@ func (db *DB) ComQuery(c *mysqlconn.Conn, query string) (*sqltypes.Result, error } // Nothing matched. - return nil, fmt.Errorf("query: %s is not supported on %v", query, db.name) + return nil, fmt.Errorf("query: '%s' is not supported on %v", query, db.name) } // @@ -260,17 +280,32 @@ func (db *DB) ComQuery(c *mysqlconn.Conn, query string) (*sqltypes.Result, error // // AddQuery adds a query and its expected result. -func (db *DB) AddQuery(query string, expectedResult *sqltypes.Result) { +func (db *DB) AddQuery(query string, expectedResult *sqltypes.Result) *ExpectedResult { if len(expectedResult.Rows) > 0 && len(expectedResult.Fields) == 0 { panic(fmt.Errorf("Please add Fields to this Result so it's valid: %v", query)) } - result := &sqltypes.Result{} - *result = *expectedResult + resultCopy := &sqltypes.Result{} + *resultCopy = *expectedResult db.mu.Lock() defer db.mu.Unlock() key := strings.ToLower(query) - db.data[key] = result + r := &ExpectedResult{resultCopy, nil} + db.data[key] = r db.queryCalled[key] = 0 + return r +} + +// SetBeforeFunc sets the BeforeFunc field for the previously registered "query". +func (db *DB) SetBeforeFunc(query string, f func()) { + db.mu.Lock() + defer db.mu.Unlock() + key := strings.ToLower(query) + r, ok := db.data[key] + if !ok { + db.t.Fatalf("BUG: no query registered for: %v", query) + } + + r.BeforeFunc = f } // AddQueryPattern adds an expected result for a set of queries. diff --git a/go/mysqlconn/query.go b/go/mysqlconn/query.go index ed38a861487..b64f8507c7e 100644 --- a/go/mysqlconn/query.go +++ b/go/mysqlconn/query.go @@ -235,7 +235,28 @@ func (c *Conn) parseRow(data []byte, fields []*querypb.Field) ([]sqltypes.Value, } // ExecuteFetch is the same as sqldb.Conn.ExecuteFetch. -// Returns a sqldb.SQLError. +// Returns a sqldb.SQLError. Depending on the transport used, the error +// returned might be different for the same condition: +// +// 1. if the server closes the connection when no command is in flight: +// +// 1.1 unix: writeComQuery will fail with a 'broken pipe', and we'll +// return CRServerGone(2006). +// +// 1.2 tcp: writeComQuery will most likely work, but readComQueryResponse +// will fail, and we'll return CRServerLost(2013). +// +// This is because closing a TCP socket on the server side sends +// a FIN to the client (telling the client the server is done +// writing), but on most platforms doesn't send a RST. So the +// client has no idea it can't write. So it succeeds writing data, which +// *then* triggers the server to send a RST back, received a bit +// later. By then, the client has already started waiting for +// the response, and will just return a CRServerLost(2013). +// So CRServerGone(2006) will almost never be seen with TCP. +// +// 2. if the server closes the connection when a command is in flight, +// readComQueryResponse will fail, and we'll return CRServerLost(2013). func (c *Conn) ExecuteFetch(query string, maxrows int, wantfields bool) (*sqltypes.Result, error) { // This is a new command, need to reset the sequence. c.sequence = 0 diff --git a/go/mysqlconn/replication/binlog_event_rbr.go b/go/mysqlconn/replication/binlog_event_rbr.go index 11df0655176..d23f7f765c6 100644 --- a/go/mysqlconn/replication/binlog_event_rbr.go +++ b/go/mysqlconn/replication/binlog_event_rbr.go @@ -1,16 +1,21 @@ package replication import ( + "bytes" "encoding/binary" "fmt" "math" "strconv" + "time" "github.com/youtube/vitess/go/sqltypes" querypb "github.com/youtube/vitess/go/vt/proto/query" ) +// ZeroTimestamp is the special value 0 for a timestamp. +var ZeroTimestamp = []byte("0000-00-00 00:00:00") + // TableMap implements BinlogEvent.TableMap(). // // Expected format (L = total length of event data): @@ -187,10 +192,8 @@ func cellLength(data []byte, pos int, typ byte, metadata uint16) (int, error) { return 4, nil case TypeLongLong, TypeDouble: return 8, nil - case TypeDate, TypeNewDate: + case TypeDate, TypeTime, TypeNewDate: return 3, nil - case TypeTime: - return 4, nil case TypeDateTime: return 8, nil case TypeVarchar, TypeVarString: @@ -281,14 +284,11 @@ func cellLength(data []byte, pos int, typ byte, metadata uint16) (int, error) { // This may do String, Enum, and Set. The type is in // metadata. If it's a string, then there will be more bits. // This will give us the maximum length of the field. - max := 0 t := metadata >> 8 if t == TypeEnum || t == TypeSet { - max = int(metadata & 0xff) - } else { - max = int((((metadata >> 4) & 0x300) ^ 0x300) + (metadata & 0xff)) + return int(metadata & 0xff), nil } - + max := int((((metadata >> 4) & 0x300) ^ 0x300) + (metadata & 0xff)) // Length is encoded in 1 or 2 bytes. if max > 255 { l := int(uint64(data[pos]) | @@ -303,6 +303,22 @@ func cellLength(data []byte, pos int, typ byte, metadata uint16) (int, error) { } } +// printTimestamp is a helper method to append a timestamp into a bytes.Buffer, +// and return the Buffer. +func printTimestamp(v uint32) *bytes.Buffer { + if v == 0 { + return bytes.NewBuffer(ZeroTimestamp) + } + + t := time.Unix(int64(v), 0).UTC() + year, month, day := t.Date() + hour, minute, second := t.Clock() + + result := &bytes.Buffer{} + fmt.Fprintf(result, "%04d-%02d-%02d %02d:%02d:%02d", year, int(month), day, hour, minute, second) + return result +} + // CellValue returns the data for a cell as a sqltypes.Value, and how // many bytes it takes. It only uses the querypb.Type value for the // signed flag. @@ -316,6 +332,11 @@ func CellValue(data []byte, pos int, typ byte, metadata uint16, styp querypb.Typ return sqltypes.MakeTrusted(querypb.Type_UINT8, strconv.AppendUint(nil, uint64(data[pos]), 10)), 1, nil case TypeYear: + val := data[pos] + if val == 0 { + return sqltypes.MakeTrusted(querypb.Type_YEAR, + []byte{'0', '0', '0', '0'}), 1, nil + } return sqltypes.MakeTrusted(querypb.Type_YEAR, strconv.AppendUint(nil, uint64(data[pos])+1900, 10)), 1, nil case TypeShort: @@ -362,8 +383,9 @@ func CellValue(data []byte, pos int, typ byte, metadata uint16, styp querypb.Typ strconv.AppendFloat(nil, fval, 'E', -1, 64)), 8, nil case TypeTimestamp: val := binary.LittleEndian.Uint32(data[pos : pos+4]) + txt := printTimestamp(val) return sqltypes.MakeTrusted(querypb.Type_TIMESTAMP, - strconv.AppendUint(nil, uint64(val), 10)), 4, nil + txt.Bytes()), 4, nil case TypeLongLong: val := binary.LittleEndian.Uint64(data[pos : pos+8]) if sqltypes.IsSigned(styp) { @@ -382,12 +404,26 @@ func CellValue(data []byte, pos int, typ byte, metadata uint16, styp querypb.Typ return sqltypes.MakeTrusted(querypb.Type_DATE, []byte(fmt.Sprintf("%04d-%02d-%02d", year, month, day))), 3, nil case TypeTime: - val := binary.LittleEndian.Uint32(data[pos : pos+4]) - hour := val / 10000 - minute := (val % 10000) / 100 - second := val % 100 + var hour, minute, second int32 + if data[pos+2]&128 > 0 { + // Negative number, have to extend the sign. + val := int32(uint32(data[pos]) + + uint32(data[pos+1])<<8 + + uint32(data[pos+2])<<16 + + uint32(255)<<24) + hour = val / 10000 + minute = -((val % 10000) / 100) + second = -(val % 100) + } else { + val := int32(data[pos]) + + int32(data[pos+1])<<8 + + int32(data[pos+2])<<16 + hour = val / 10000 + minute = (val % 10000) / 100 + second = val % 100 + } return sqltypes.MakeTrusted(querypb.Type_TIME, - []byte(fmt.Sprintf("%02d:%02d:%02d", hour, minute, second))), 4, nil + []byte(fmt.Sprintf("%02d:%02d:%02d", hour, minute, second))), 3, nil case TypeDateTime: val := binary.LittleEndian.Uint64(data[pos : pos+8]) d := val / 1000000 @@ -418,47 +454,54 @@ func CellValue(data []byte, pos int, typ byte, metadata uint16, styp querypb.Typ return sqltypes.MakeTrusted(querypb.Type_BIT, data[pos:pos+l]), l, nil case TypeTimestamp2: - second := binary.LittleEndian.Uint32(data[pos : pos+4]) + second := binary.BigEndian.Uint32(data[pos : pos+4]) + txt := printTimestamp(second) switch metadata { case 1: decimals := int(data[pos+4]) + fmt.Fprintf(txt, ".%01d", decimals/10) return sqltypes.MakeTrusted(querypb.Type_TIMESTAMP, - []byte(fmt.Sprintf("%v.%01d", second, decimals))), 5, nil + txt.Bytes()), 5, nil case 2: decimals := int(data[pos+4]) + fmt.Fprintf(txt, ".%02d", decimals) return sqltypes.MakeTrusted(querypb.Type_TIMESTAMP, - []byte(fmt.Sprintf("%v.%02d", second, decimals))), 5, nil + txt.Bytes()), 5, nil case 3: - decimals := int(data[pos+4]) + - int(data[pos+5])<<8 + decimals := int(data[pos+4])<<8 + + int(data[pos+5]) + fmt.Fprintf(txt, ".%03d", decimals/10) return sqltypes.MakeTrusted(querypb.Type_TIMESTAMP, - []byte(fmt.Sprintf("%v.%03d", second, decimals))), 6, nil + txt.Bytes()), 6, nil case 4: - decimals := int(data[pos+4]) + - int(data[pos+5])<<8 + decimals := int(data[pos+4])<<8 + + int(data[pos+5]) + fmt.Fprintf(txt, ".%04d", decimals) return sqltypes.MakeTrusted(querypb.Type_TIMESTAMP, - []byte(fmt.Sprintf("%v.%04d", second, decimals))), 6, nil + txt.Bytes()), 6, nil case 5: - decimals := int(data[pos+4]) + + decimals := int(data[pos+4])<<16 + int(data[pos+5])<<8 + - int(data[pos+6])<<16 + int(data[pos+6]) + fmt.Fprintf(txt, ".%05d", decimals/10) return sqltypes.MakeTrusted(querypb.Type_TIMESTAMP, - []byte(fmt.Sprintf("%v.%05d", second, decimals))), 7, nil + txt.Bytes()), 7, nil case 6: - decimals := int(data[pos+4]) + + decimals := int(data[pos+4])<<16 + int(data[pos+5])<<8 + - int(data[pos+6])<<16 + int(data[pos+6]) + fmt.Fprintf(txt, ".%06d", decimals) return sqltypes.MakeTrusted(querypb.Type_TIMESTAMP, - []byte(fmt.Sprintf("%v.%.6d", second, decimals))), 7, nil + txt.Bytes()), 7, nil } return sqltypes.MakeTrusted(querypb.Type_TIMESTAMP, - strconv.AppendUint(nil, uint64(second), 10)), 4, nil + txt.Bytes()), 4, nil case TypeDateTime2: - ymdhms := (uint64(data[pos]) | - uint64(data[pos+1])<<8 | + ymdhms := (uint64(data[pos])<<32 | + uint64(data[pos+1])<<24 | uint64(data[pos+2])<<16 | - uint64(data[pos+3])<<24 | - uint64(data[pos+4])<<32) - uint64(0x8000000000) + uint64(data[pos+3])<<8 | + uint64(data[pos+4])) - uint64(0x8000000000) ymd := ymdhms >> 17 ym := ymd >> 5 hms := ymdhms % (1 << 17) @@ -471,46 +514,53 @@ func CellValue(data []byte, pos int, typ byte, metadata uint16, styp querypb.Typ minute := (hms >> 6) % (1 << 6) hour := hms >> 12 - datetime := fmt.Sprintf("%04d-%02d-%02d %02d:%02d:%02d", year, month, day, hour, minute, second) + txt := &bytes.Buffer{} + fmt.Fprintf(txt, "%04d-%02d-%02d %02d:%02d:%02d", year, month, day, hour, minute, second) switch metadata { case 1: decimals := int(data[pos+5]) + fmt.Fprintf(txt, ".%01d", decimals/10) return sqltypes.MakeTrusted(querypb.Type_DATETIME, - []byte(fmt.Sprintf("%v.%01d", datetime, decimals))), 6, nil + txt.Bytes()), 6, nil case 2: decimals := int(data[pos+5]) + fmt.Fprintf(txt, ".%02d", decimals) return sqltypes.MakeTrusted(querypb.Type_DATETIME, - []byte(fmt.Sprintf("%v.%02d", datetime, decimals))), 6, nil + txt.Bytes()), 6, nil case 3: - decimals := int(data[pos+5]) + - int(data[pos+6])<<8 + decimals := int(data[pos+5])<<8 + + int(data[pos+6]) + fmt.Fprintf(txt, ".%03d", decimals/10) return sqltypes.MakeTrusted(querypb.Type_DATETIME, - []byte(fmt.Sprintf("%v.%03d", datetime, decimals))), 7, nil + txt.Bytes()), 7, nil case 4: - decimals := int(data[pos+5]) + - int(data[pos+6])<<8 + decimals := int(data[pos+5])<<8 + + int(data[pos+6]) + fmt.Fprintf(txt, ".%04d", decimals) return sqltypes.MakeTrusted(querypb.Type_DATETIME, - []byte(fmt.Sprintf("%v.%04d", datetime, decimals))), 7, nil + txt.Bytes()), 7, nil case 5: - decimals := int(data[pos+5]) + + decimals := int(data[pos+5])<<16 + int(data[pos+6])<<8 + - int(data[pos+7])<<16 + int(data[pos+7]) + fmt.Fprintf(txt, ".%05d", decimals/10) return sqltypes.MakeTrusted(querypb.Type_DATETIME, - []byte(fmt.Sprintf("%v.%05d", datetime, decimals))), 8, nil + txt.Bytes()), 8, nil case 6: - decimals := int(data[pos+5]) + + decimals := int(data[pos+5])<<16 + int(data[pos+6])<<8 + - int(data[pos+7])<<16 + int(data[pos+7]) + fmt.Fprintf(txt, ".%06d", decimals) return sqltypes.MakeTrusted(querypb.Type_DATETIME, - []byte(fmt.Sprintf("%v.%.6d", datetime, decimals))), 8, nil + txt.Bytes()), 8, nil } return sqltypes.MakeTrusted(querypb.Type_DATETIME, - []byte(datetime)), 5, nil + txt.Bytes()), 5, nil case TypeTime2: - hms := (int64(data[pos]) | + hms := (int64(data[pos])<<16 | int64(data[pos+1])<<8 | - int64(data[pos+2])<<16) - 0x800000 + int64(data[pos+2])) - 0x800000 sign := "" if hms < 0 { hms = -hms @@ -534,34 +584,34 @@ func CellValue(data []byte, pos int, typ byte, metadata uint16, styp querypb.Typ } fracStr = fmt.Sprintf(".%.2d", frac) case 3: - frac := int(data[pos+3]) | - int(data[pos+4])<<8 + frac := int(data[pos+3])<<8 | + int(data[pos+4]) if sign == "-" && frac != 0 { hms-- frac = 0x10000 - frac } fracStr = fmt.Sprintf(".%.3d", frac/10) case 4: - frac := int(data[pos+3]) | - int(data[pos+4])<<8 + frac := int(data[pos+3])<<8 | + int(data[pos+4]) if sign == "-" && frac != 0 { hms-- frac = 0x10000 - frac } fracStr = fmt.Sprintf(".%.4d", frac) case 5: - frac := int(data[pos+3]) | + frac := int(data[pos+3])<<16 | int(data[pos+4])<<8 | - int(data[pos+5])<<16 + int(data[pos+5]) if sign == "-" && frac != 0 { hms-- frac = 0x1000000 - frac } fracStr = fmt.Sprintf(".%.5d", frac/10) case 6: - frac := int(data[pos+3]) | + frac := int(data[pos+3])<<16 | int(data[pos+4])<<8 | - int(data[pos+5])<<16 + int(data[pos+5]) if sign == "-" && frac != 0 { hms-- frac = 0x1000000 - frac @@ -576,14 +626,18 @@ func CellValue(data []byte, pos int, typ byte, metadata uint16, styp querypb.Typ []byte(fmt.Sprintf("%v%02d:%02d:%02d%v", sign, hour, minute, second, fracStr))), 3 + (int(metadata)+1)/2, nil case TypeJSON: + l := int(uint64(data[pos]) | + uint64(data[pos+1])<<8) // length in encoded in 'meta' bytes, but at least 2, // and the value cannot be > 64k, so just read 2 bytes. // (meta also should have '2' as value). // (this weird logic is what event printing does). - l := int(uint64(data[pos]) | - uint64(data[pos+1])<<8) - return sqltypes.MakeTrusted(querypb.Type_JSON, - data[pos+int(metadata):pos+int(metadata)+l]), l + int(metadata), nil + + // TODO(alainjobart) the binary data for JSON should + // be parsed, and re-printed as JSON. This is a large + // project, as the binary version of the data is + // somewhat complex. For now, just return NULL. + return sqltypes.NULL, l + int(metadata), nil case TypeNewDecimal: precision := int(metadata >> 8) // total digits number @@ -601,12 +655,13 @@ func CellValue(data []byte, pos int, typ byte, metadata uint16, styp querypb.Typ d := make([]byte, l) copy(d, data[pos:pos+l]) - result := []byte{} + txt := &bytes.Buffer{} + isNegative := (d[0] & 0x80) == 0 d[0] ^= 0x80 // First bit is inverted. if isNegative { // Negative numbers are just inverted bytes. - result = append(result, '-') + txt.WriteByte('-') for i := range d { d[i] ^= 0xff } @@ -638,55 +693,52 @@ func CellValue(data []byte, pos int, typ byte, metadata uint16, styp querypb.Typ } pos = dig2bytes[intg0x] if val > 0 { - result = strconv.AppendUint(result, uint64(val), 10) + txt.Write(strconv.AppendUint(nil, uint64(val), 10)) } // now the full digits, 32 bits each, 9 digits for i := 0; i < intg0; i++ { val = binary.BigEndian.Uint32(d[pos : pos+4]) - t := fmt.Sprintf("%9d", val) - result = append(result, []byte(t)...) + fmt.Fprintf(txt, "%9d", val) pos += 4 } // now see if we have a fraction if scale == 0 { return sqltypes.MakeTrusted(querypb.Type_DECIMAL, - result), l, nil + txt.Bytes()), l, nil } - result = append(result, '.') + txt.WriteByte('.') // now the full fractional digits for i := 0; i < frac0; i++ { val = binary.BigEndian.Uint32(d[pos : pos+4]) - t := fmt.Sprintf("%9d", val) - result = append(result, []byte(t)...) + fmt.Fprintf(txt, "%9d", val) pos += 4 } // then the partial fractional digits - t := "" switch dig2bytes[frac0x] { case 0: // Nothing to do return sqltypes.MakeTrusted(querypb.Type_DECIMAL, - result), l, nil + txt.Bytes()), l, nil case 1: // one byte, 1 or 2 digits val = uint32(d[pos]) if frac0x == 1 { - t = fmt.Sprintf("%1d", val) + fmt.Fprintf(txt, "%1d", val) } else { - t = fmt.Sprintf("%2d", val) + fmt.Fprintf(txt, "%2d", val) } case 2: // two bytes, 3 or 4 digits val = uint32(d[pos])<<8 + uint32(d[pos+1]) if frac0x == 3 { - t = fmt.Sprintf("%3d", val) + fmt.Fprintf(txt, "%3d", val) } else { - t = fmt.Sprintf("%4d", val) + fmt.Fprintf(txt, "%4d", val) } case 3: // 3 bytes, 5 or 6 digits @@ -694,9 +746,9 @@ func CellValue(data []byte, pos int, typ byte, metadata uint16, styp querypb.Typ uint32(d[pos+1])<<8 + uint32(d[pos+2]) if frac0x == 5 { - t = fmt.Sprintf("%5d", val) + fmt.Fprintf(txt, "%5d", val) } else { - t = fmt.Sprintf("%6d", val) + fmt.Fprintf(txt, "%6d", val) } case 4: // 4 bytes, 7 or 8 digits (9 digits would be a full) @@ -705,15 +757,14 @@ func CellValue(data []byte, pos int, typ byte, metadata uint16, styp querypb.Typ uint32(d[pos+2])<<8 + uint32(d[pos+3]) if frac0x == 7 { - t = fmt.Sprintf("%7d", val) + fmt.Fprintf(txt, "%7d", val) } else { - t = fmt.Sprintf("%8d", val) + fmt.Fprintf(txt, "%8d", val) } } - result = append(result, []byte(t)...) return sqltypes.MakeTrusted(querypb.Type_DECIMAL, - result), l, nil + txt.Bytes()), l, nil case TypeEnum: switch metadata & 0xff { @@ -766,24 +817,32 @@ func CellValue(data []byte, pos int, typ byte, metadata uint16, styp querypb.Typ // metadata. If it's a string, then there will be more bits. t := metadata >> 8 if t == TypeEnum { + // We don't know the string values. So just use the + // numbers. switch metadata & 0xff { case 1: // One byte storage. - return sqltypes.MakeTrusted(querypb.Type_ENUM, + return sqltypes.MakeTrusted(querypb.Type_UINT8, strconv.AppendUint(nil, uint64(data[pos]), 10)), 1, nil case 2: // Two bytes storage. val := binary.LittleEndian.Uint16(data[pos : pos+2]) - return sqltypes.MakeTrusted(querypb.Type_ENUM, + return sqltypes.MakeTrusted(querypb.Type_UINT16, strconv.AppendUint(nil, uint64(val), 10)), 2, nil default: return sqltypes.NULL, 0, fmt.Errorf("unexpected enum size: %v", metadata&0xff) } } if t == TypeSet { + // We don't know the set values. So just use the + // numbers. l := int(metadata & 0xff) - return sqltypes.MakeTrusted(querypb.Type_BIT, - data[pos:pos+l]), l, nil + var val uint64 + for i := 0; i < l; i++ { + val += uint64(data[pos+i]) << (uint(i) * 8) + } + return sqltypes.MakeTrusted(querypb.Type_UINT64, + strconv.AppendUint(nil, uint64(val), 10)), l, nil } // This is a real string. The length is weird. max := int((((metadata >> 4) & 0x300) ^ 0x300) + (metadata & 0xff)) diff --git a/go/mysqlconn/replication/binlog_event_rbr_test.go b/go/mysqlconn/replication/binlog_event_rbr_test.go index 7c92b69b803..8d3b1972eb9 100644 --- a/go/mysqlconn/replication/binlog_event_rbr_test.go +++ b/go/mysqlconn/replication/binlog_event_rbr_test.go @@ -82,10 +82,11 @@ func TestCellLengthAndData(t *testing.T) { out: sqltypes.MakeTrusted(querypb.Type_FLOAT64, []byte("3.1415926535E+00")), }, { + // 0x58d137c5 = 1490106309 = 2017-03-21 14:25:09 typ: TypeTimestamp, - data: []byte{0x84, 0x83, 0x82, 0x81}, + data: []byte{0xc5, 0x37, 0xd1, 0x58}, out: sqltypes.MakeTrusted(querypb.Type_TIMESTAMP, - []byte(fmt.Sprintf("%v", 0x81828384))), + []byte("2017-03-21 14:25:09")), }, { typ: TypeLongLong, styp: querypb.Type_UINT64, @@ -112,8 +113,8 @@ func TestCellLengthAndData(t *testing.T) { []byte("2010-10-03")), }, { typ: TypeTime, - // 154532 = 0x00025ba4 - data: []byte{0xa4, 0x5b, 0x02, 0x00}, + // 154532 = 0x025ba4 + data: []byte{0xa4, 0x5b, 0x02}, out: sqltypes.MakeTrusted(querypb.Type_TIME, []byte("15:45:32")), }, { @@ -141,98 +142,99 @@ func TestCellLengthAndData(t *testing.T) { out: sqltypes.MakeTrusted(querypb.Type_BIT, []byte{3, 1}), }, { + // 0x58d137c5 = 1490106309 = 2017-03-21 14:25:09 typ: TypeTimestamp2, metadata: 0, - data: []byte{0x84, 0x83, 0x82, 0x81}, + data: []byte{0x58, 0xd1, 0x37, 0xc5}, out: sqltypes.MakeTrusted(querypb.Type_TIMESTAMP, - []byte(fmt.Sprintf("%v", 0x81828384))), + []byte("2017-03-21 14:25:09")), }, { typ: TypeTimestamp2, metadata: 1, - data: []byte{0x84, 0x83, 0x82, 0x81, 7}, + data: []byte{0x58, 0xd1, 0x37, 0xc5, 70}, out: sqltypes.MakeTrusted(querypb.Type_TIMESTAMP, - []byte(fmt.Sprintf("%v.7", 0x81828384))), + []byte("2017-03-21 14:25:09.7")), }, { typ: TypeTimestamp2, metadata: 2, - data: []byte{0x84, 0x83, 0x82, 0x81, 76}, + data: []byte{0x58, 0xd1, 0x37, 0xc5, 76}, out: sqltypes.MakeTrusted(querypb.Type_TIMESTAMP, - []byte(fmt.Sprintf("%v.76", 0x81828384))), + []byte("2017-03-21 14:25:09.76")), }, { typ: TypeTimestamp2, metadata: 3, - // 765 = 0x02fd - data: []byte{0x84, 0x83, 0x82, 0x81, 0xfd, 0x02}, + // 7650 = 0x1de2 + data: []byte{0x58, 0xd1, 0x37, 0xc5, 0x1d, 0xe2}, out: sqltypes.MakeTrusted(querypb.Type_TIMESTAMP, - []byte(fmt.Sprintf("%v.765", 0x81828384))), + []byte("2017-03-21 14:25:09.765")), }, { typ: TypeTimestamp2, metadata: 4, // 7654 = 0x1de6 - data: []byte{0x84, 0x83, 0x82, 0x81, 0xe6, 0x1d}, + data: []byte{0x58, 0xd1, 0x37, 0xc5, 0x1d, 0xe6}, out: sqltypes.MakeTrusted(querypb.Type_TIMESTAMP, - []byte(fmt.Sprintf("%v.7654", 0x81828384))), + []byte("2017-03-21 14:25:09.7654")), }, { typ: TypeTimestamp2, metadata: 5, - // 76543 = 0x012aff - data: []byte{0x84, 0x83, 0x82, 0x81, 0xff, 0x2a, 0x01}, + // 76540 = 0x0badf6 + data: []byte{0x58, 0xd1, 0x37, 0xc5, 0x0b, 0xad, 0xf6}, out: sqltypes.MakeTrusted(querypb.Type_TIMESTAMP, - []byte(fmt.Sprintf("%v.76543", 0x81828384))), + []byte("2017-03-21 14:25:09.76543")), }, { typ: TypeTimestamp2, metadata: 6, // 765432 = 0x0badf8 - data: []byte{0x84, 0x83, 0x82, 0x81, 0xf8, 0xad, 0x0b}, + data: []byte{0x58, 0xd1, 0x37, 0xc5, 0x0b, 0xad, 0xf8}, out: sqltypes.MakeTrusted(querypb.Type_TIMESTAMP, - []byte(fmt.Sprintf("%v.765432", 0x81828384))), + []byte("2017-03-21 14:25:09.765432")), }, { typ: TypeDateTime2, metadata: 0, // (2012 * 13 + 6) << 22 + 21 << 17 + 15 << 12 + 45 << 6 + 17) // = 109734198097 = 0x198caafb51 // Then have to add 0x8000000000 = 0x998caafb51 - data: []byte{0x51, 0xfb, 0xaa, 0x8c, 0x99}, + data: []byte{0x99, 0x8c, 0xaa, 0xfb, 0x51}, out: sqltypes.MakeTrusted(querypb.Type_DATETIME, []byte("2012-06-21 15:45:17")), }, { typ: TypeDateTime2, metadata: 1, - data: []byte{0x51, 0xfb, 0xaa, 0x8c, 0x99, 7}, + data: []byte{0x99, 0x8c, 0xaa, 0xfb, 0x51, 70}, out: sqltypes.MakeTrusted(querypb.Type_DATETIME, []byte("2012-06-21 15:45:17.7")), }, { typ: TypeDateTime2, metadata: 2, - data: []byte{0x51, 0xfb, 0xaa, 0x8c, 0x99, 76}, + data: []byte{0x99, 0x8c, 0xaa, 0xfb, 0x51, 76}, out: sqltypes.MakeTrusted(querypb.Type_DATETIME, []byte("2012-06-21 15:45:17.76")), }, { typ: TypeDateTime2, metadata: 3, - // 765 = 0x02fd - data: []byte{0x51, 0xfb, 0xaa, 0x8c, 0x99, 0xfd, 0x02}, + // 7650 = 0x1de2 + data: []byte{0x99, 0x8c, 0xaa, 0xfb, 0x51, 0x1d, 0xe2}, out: sqltypes.MakeTrusted(querypb.Type_DATETIME, []byte("2012-06-21 15:45:17.765")), }, { typ: TypeDateTime2, metadata: 4, // 7654 = 0x1de6 - data: []byte{0x51, 0xfb, 0xaa, 0x8c, 0x99, 0xe6, 0x1d}, + data: []byte{0x99, 0x8c, 0xaa, 0xfb, 0x51, 0x1d, 0xe6}, out: sqltypes.MakeTrusted(querypb.Type_DATETIME, []byte("2012-06-21 15:45:17.7654")), }, { typ: TypeDateTime2, metadata: 5, - // 76543 = 0x012aff - data: []byte{0x51, 0xfb, 0xaa, 0x8c, 0x99, 0xff, 0x2a, 0x01}, + // 765430 = 0x0badf6 + data: []byte{0x99, 0x8c, 0xaa, 0xfb, 0x51, 0x0b, 0xad, 0xf6}, out: sqltypes.MakeTrusted(querypb.Type_DATETIME, []byte("2012-06-21 15:45:17.76543")), }, { typ: TypeDateTime2, metadata: 6, // 765432 = 0x0badf8 - data: []byte{0x51, 0xfb, 0xaa, 0x8c, 0x99, 0xf8, 0xad, 0x0b}, + data: []byte{0x99, 0x8c, 0xaa, 0xfb, 0x51, 0x0b, 0xad, 0xf8}, out: sqltypes.MakeTrusted(querypb.Type_DATETIME, []byte("2012-06-21 15:45:17.765432")), }, { @@ -248,130 +250,130 @@ func TestCellLengthAndData(t *testing.T) { // 7FFFFE.F6 -2 246 -00:00:01.10 FFFFFFFFFE.FE7960 typ: TypeTime2, metadata: 2, - data: []byte{0x00, 0x00, 0x80, 0x00}, + data: []byte{0x80, 0x00, 0x00, 0x00}, out: sqltypes.MakeTrusted(querypb.Type_TIME, []byte("00:00:00.00")), }, { typ: TypeTime2, metadata: 2, - data: []byte{0xff, 0xff, 0x7f, 0xff}, + data: []byte{0x7f, 0xff, 0xff, 0xff}, out: sqltypes.MakeTrusted(querypb.Type_TIME, []byte("-00:00:00.01")), }, { typ: TypeTime2, metadata: 2, - data: []byte{0xff, 0xff, 0x7f, 0x9d}, + data: []byte{0x7f, 0xff, 0xff, 0x9d}, out: sqltypes.MakeTrusted(querypb.Type_TIME, []byte("-00:00:00.99")), }, { typ: TypeTime2, metadata: 2, - data: []byte{0xff, 0xff, 0x7f, 0x00}, + data: []byte{0x7f, 0xff, 0xff, 0x00}, out: sqltypes.MakeTrusted(querypb.Type_TIME, []byte("-00:00:01.00")), }, { typ: TypeTime2, metadata: 2, - data: []byte{0xfe, 0xff, 0x7f, 0xff}, + data: []byte{0x7f, 0xff, 0xfe, 0xff}, out: sqltypes.MakeTrusted(querypb.Type_TIME, []byte("-00:00:01.01")), }, { typ: TypeTime2, metadata: 2, - data: []byte{0xfe, 0xff, 0x7f, 0xf6}, + data: []byte{0x7f, 0xff, 0xfe, 0xf6}, out: sqltypes.MakeTrusted(querypb.Type_TIME, []byte("-00:00:01.10")), }, { // Similar tests for 4 decimals. typ: TypeTime2, metadata: 4, - data: []byte{0x00, 0x00, 0x80, 0x00, 0x00}, + data: []byte{0x80, 0x00, 0x00, 0x00, 0x00}, out: sqltypes.MakeTrusted(querypb.Type_TIME, []byte("00:00:00.0000")), }, { typ: TypeTime2, metadata: 4, - data: []byte{0xff, 0xff, 0x7f, 0xff, 0xff}, + data: []byte{0x7f, 0xff, 0xff, 0xff, 0xff}, out: sqltypes.MakeTrusted(querypb.Type_TIME, []byte("-00:00:00.0001")), }, { typ: TypeTime2, metadata: 4, - data: []byte{0xff, 0xff, 0x7f, 0x9d, 0xff}, + data: []byte{0x7f, 0xff, 0xff, 0xff, 0x9d}, out: sqltypes.MakeTrusted(querypb.Type_TIME, []byte("-00:00:00.0099")), }, { typ: TypeTime2, metadata: 4, - data: []byte{0xff, 0xff, 0x7f, 0x00, 0x00}, + data: []byte{0x7f, 0xff, 0xff, 0x00, 0x00}, out: sqltypes.MakeTrusted(querypb.Type_TIME, []byte("-00:00:01.0000")), }, { typ: TypeTime2, metadata: 4, - data: []byte{0xfe, 0xff, 0x7f, 0xff, 0xff}, + data: []byte{0x7f, 0xff, 0xfe, 0xff, 0xff}, out: sqltypes.MakeTrusted(querypb.Type_TIME, []byte("-00:00:01.0001")), }, { typ: TypeTime2, metadata: 4, - data: []byte{0xfe, 0xff, 0x7f, 0xf6, 0xff}, + data: []byte{0x7f, 0xff, 0xfe, 0xff, 0xf6}, out: sqltypes.MakeTrusted(querypb.Type_TIME, []byte("-00:00:01.0010")), }, { // Similar tests for 6 decimals. typ: TypeTime2, metadata: 6, - data: []byte{0x00, 0x00, 0x80, 0x00, 0x00, 0x00}, + data: []byte{0x80, 0x00, 0x00, 0x00, 0x00, 0x00}, out: sqltypes.MakeTrusted(querypb.Type_TIME, []byte("00:00:00.000000")), }, { typ: TypeTime2, metadata: 6, - data: []byte{0xff, 0xff, 0x7f, 0xff, 0xff, 0xff}, + data: []byte{0x7f, 0xff, 0xff, 0xff, 0xff, 0xff}, out: sqltypes.MakeTrusted(querypb.Type_TIME, []byte("-00:00:00.000001")), }, { typ: TypeTime2, metadata: 6, - data: []byte{0xff, 0xff, 0x7f, 0x9d, 0xff, 0xff}, + data: []byte{0x7f, 0xff, 0xff, 0xff, 0xff, 0x9d}, out: sqltypes.MakeTrusted(querypb.Type_TIME, []byte("-00:00:00.000099")), }, { typ: TypeTime2, metadata: 6, - data: []byte{0xff, 0xff, 0x7f, 0x00, 0x00, 0x00}, + data: []byte{0x7f, 0xff, 0xff, 0x00, 0x00, 0x00}, out: sqltypes.MakeTrusted(querypb.Type_TIME, []byte("-00:00:01.000000")), }, { typ: TypeTime2, metadata: 6, - data: []byte{0xfe, 0xff, 0x7f, 0xff, 0xff, 0xff}, + data: []byte{0x7f, 0xff, 0xfe, 0xff, 0xff, 0xff}, out: sqltypes.MakeTrusted(querypb.Type_TIME, []byte("-00:00:01.000001")), }, { typ: TypeTime2, metadata: 6, - data: []byte{0xfe, 0xff, 0x7f, 0xf6, 0xff, 0xff}, + data: []byte{0x7f, 0xff, 0xfe, 0xff, 0xff, 0xf6}, out: sqltypes.MakeTrusted(querypb.Type_TIME, []byte("-00:00:01.000010")), }, { // Few more tests. typ: TypeTime2, metadata: 0, - data: []byte{0x00, 0x00, 0x80}, + data: []byte{0x80, 0x00, 0x00}, out: sqltypes.MakeTrusted(querypb.Type_TIME, []byte("00:00:00")), }, { typ: TypeTime2, metadata: 1, - data: []byte{0x01, 0x00, 0x80, 0x0a}, + data: []byte{0x80, 0x00, 0x01, 0x0a}, out: sqltypes.MakeTrusted(querypb.Type_TIME, []byte("00:00:01.1")), }, { typ: TypeTime2, metadata: 2, - data: []byte{0x01, 0x00, 0x80, 0x0a}, + data: []byte{0x80, 0x00, 0x01, 0x0a}, out: sqltypes.MakeTrusted(querypb.Type_TIME, []byte("00:00:01.10")), }, { @@ -379,15 +381,14 @@ func TestCellLengthAndData(t *testing.T) { metadata: 0, // 15 << 12 + 34 << 6 + 54 = 63670 = 0x00f8b6 // and need to add 0x800000 - data: []byte{0xb6, 0xf8, 0x80}, + data: []byte{0x80, 0xf8, 0xb6}, out: sqltypes.MakeTrusted(querypb.Type_TIME, []byte("15:34:54")), }, { typ: TypeJSON, metadata: 2, data: []byte{0x03, 0x00, 'a', 'b', 'c'}, - out: sqltypes.MakeTrusted(querypb.Type_JSON, - []byte("abc")), + out: sqltypes.NULL, }, { typ: TypeEnum, metadata: 1, diff --git a/go/mysqlconn/replication_test.go b/go/mysqlconn/replication_test.go index ae9f0a93a5b..0b9b802e874 100644 --- a/go/mysqlconn/replication_test.go +++ b/go/mysqlconn/replication_test.go @@ -1,6 +1,8 @@ package mysqlconn import ( + "bytes" + "fmt" "reflect" "strings" "sync" @@ -11,6 +13,9 @@ import ( "github.com/youtube/vitess/go/mysqlconn/replication" "github.com/youtube/vitess/go/sqldb" + "github.com/youtube/vitess/go/sqltypes" + + querypb "github.com/youtube/vitess/go/vt/proto/query" ) func TestComBinlogDump(t *testing.T) { @@ -602,3 +607,627 @@ func testRowReplicationWithRealDatabase(t *testing.T, params *sqldb.ConnParams) } } + +// testRowReplicationTypesWithRealDatabase creates a table wih all +// supported data types. Then we insert a row in it. then we re-build +// the SQL for the values, re-insert these. Then we select from the +// database and make sure both rows are identical. +func testRowReplicationTypesWithRealDatabase(t *testing.T, params *sqldb.ConnParams) { + // testcases are ordered by the types numbers in constants.go. + // Number are always unsigned, as we don't pass in sqltypes.Type. + testcases := []struct { + name string + createType string + createValue string + }{{ + // TINYINT + name: "tinytiny", + createType: "TINYINT UNSIGNED", + createValue: "145", + }, { + // SMALLINT + name: "smallish", + createType: "SMALLINT UNSIGNED", + createValue: "40000", + }, { + // INT + name: "regular_int", + createType: "INT UNSIGNED", + createValue: "4000000000", + }, { + // FLOAT + name: "floating", + createType: "FLOAT", + createValue: "-3.14159E-22", + }, { + // DOUBLE + name: "doubling", + createType: "DOUBLE", + createValue: "-3.14159265359E+12", + }, { + // TIMESTAMP (zero value) + name: "timestamp_zero", + createType: "TIMESTAMP", + createValue: "'0000-00-00 00:00:00'", + }, { + // TIMESTAMP (day precision) + name: "timestamp_day", + createType: "TIMESTAMP", + createValue: "'2012-11-10 00:00:00'", + }, { + // BIGINT + name: "big_int", + createType: "BIGINT UNSIGNED", + createValue: "10000000000000000000", + }, { + // MEDIUMINT + name: "mediumish", + createType: "MEDIUMINT UNSIGNED", + createValue: "10000000", + }, { + // DATE + name: "date_regular", + createType: "DATE", + createValue: "'1920-10-24'", + }, { + // TIME + name: "time_regular", + createType: "TIME", + createValue: "'120:44:58'", + }, { + // TIME + name: "time_neg", + createType: "TIME", + createValue: "'-212:44:58'", + }, { + // DATETIME + name: "datetime0", + createType: "DATETIME", + createValue: "'1020-08-23 12:44:58'", + }, { + // YEAR zero + name: "year0", + createType: "YEAR", + createValue: "0", + }, { + // YEAR + name: "year_nonzero", + createType: "YEAR", + createValue: "2052", + }, { + // VARCHAR 8 bits + name: "shortvc", + createType: "VARCHAR(30)", + createValue: "'short varchar'", + }, { + // VARCHAR 16 bits + name: "longvc", + createType: "VARCHAR(1000)", + createValue: "'long varchar'", + }, { + // BIT + name: "bit1", + createType: "BIT", + createValue: "b'1'", + }, { + // BIT + name: "bit6", + createType: "BIT(6)", + createValue: "b'100101'", + }, { + // BIT + name: "bit8", + createType: "BIT(8)", + createValue: "b'10100101'", + }, { + // BIT + name: "bit14", + createType: "BIT(14)", + createValue: "b'10100101000111'", + }, { + // BIT + name: "bit55", + createType: "BIT(55)", + createValue: "b'1010010100110100101001101001010011010010100110100101001'", + }, { + // BIT + name: "bit64", + createType: "BIT(64)", + createValue: "b'1111111111010010100110100101001101001010011010010100110100101001'", + }, { + // DECIMAL + name: "decimal2_1", + createType: "DECIMAL(2,1)", + createValue: "1.2", + }, { + // DECIMAL neg + name: "decimal2_1_neg", + createType: "DECIMAL(2,1)", + createValue: "-5.6", + }, { + // DECIMAL + name: "decimal4_2", + createType: "DECIMAL(4,2)", + createValue: "61.52", + }, { + // DECIMAL neg + name: "decimal4_2_neg", + createType: "DECIMAL(4,2)", + createValue: "-78.94", + }, { + // DECIMAL + name: "decimal6_3", + createType: "DECIMAL(6,3)", + createValue: "611.542", + }, { + // DECIMAL neg + name: "decimal6_3_neg", + createType: "DECIMAL(6,3)", + createValue: "-478.394", + }, { + // DECIMAL + name: "decimal8_4", + createType: "DECIMAL(8,4)", + createValue: "6311.5742", + }, { + // DECIMAL neg + name: "decimal8_4_neg", + createType: "DECIMAL(8,4)", + createValue: "-4778.3894", + }, { + // DECIMAL + name: "decimal10_5", + createType: "DECIMAL(10,5)", + createValue: "63711.57342", + }, { + // DECIMAL neg + name: "decimal10_5_neg", + createType: "DECIMAL(10,5)", + createValue: "-47378.38594", + }, { + // DECIMAL + name: "decimal12_6", + createType: "DECIMAL(12,6)", + createValue: "637311.557342", + }, { + // DECIMAL neg + name: "decimal12_6_neg", + createType: "DECIMAL(12,6)", + createValue: "-473788.385794", + }, { + // DECIMAL + name: "decimal14_7", + createType: "DECIMAL(14,7)", + createValue: "6375311.5574342", + }, { + // DECIMAL neg + name: "decimal14_7_neg", + createType: "DECIMAL(14,7)", + createValue: "-4732788.3853794", + }, { + // DECIMAL + name: "decimal16_8", + createType: "DECIMAL(16,8)", + createValue: "63375311.54574342", + }, { + // DECIMAL neg + name: "decimal16_8_neg", + createType: "DECIMAL(16,8)", + createValue: "-47327788.38533794", + }, { + // DECIMAL + name: "decimal18_9", + createType: "DECIMAL(18,9)", + createValue: "633075311.545714342", + }, { + // DECIMAL neg + name: "decimal18_9_neg", + createType: "DECIMAL(18,9)", + createValue: "-473327788.385033794", + }, { + // DECIMAL + name: "decimal20_10", + createType: "DECIMAL(20,10)", + createValue: "6330375311.5405714342", + }, { + // DECIMAL neg + name: "decimal20_10_neg", + createType: "DECIMAL(20,10)", + createValue: "-4731327788.3850337294", + }, { + // DECIMAL lots of left digits + name: "decimal34_0", + createType: "DECIMAL(34,0)", + createValue: "8765432345678987654345432123456786", + }, { + // DECIMAL lots of left digits neg + name: "decimal34_0_neg", + createType: "DECIMAL(34,0)", + createValue: "-8765432345678987654345432123456786", + }, { + // DECIMAL lots of right digits + name: "decimal34_30", + createType: "DECIMAL(34,30)", + createValue: "8765.432345678987654345432123456786", + }, { + // DECIMAL lots of right digits neg + name: "decimal34_30_neg", + createType: "DECIMAL(34,30)", + createValue: "-8765.432345678987654345432123456786", + }, { + // ENUM + name: "tshirtsize", + createType: "ENUM('x-small', 'small', 'medium', 'large', 'x-larg')", + createValue: "'large'", + }, { + // SET + name: "setnumbers", + createType: "SET('one', 'two', 'three', 'four', 'five', 'six', 'seven', 'eight', 'nine', 'ten')", + createValue: "'two,three,ten'", + }, { + // TINYBLOB + name: "tiny_blob", + createType: "TINYBLOB", + createValue: "'ab\\'cd'", + }, { + // BLOB + name: "bloby", + createType: "BLOB", + createValue: "'ab\\'cd'", + }, { + // MEDIUMBLOB + name: "medium_blob", + createType: "MEDIUMBLOB", + createValue: "'ab\\'cd'", + }, { + // LONGBLOB + name: "long_blob", + createType: "LONGBLOB", + createValue: "'ab\\'cd'", + }, { + // CHAR 8 bits + name: "shortchar", + createType: "CHAR(30)", + createValue: "'short char'", + }, { + // CHAR 9 bits (100 * 3 = 300, 256<=300<512) + name: "mediumchar", + createType: "CHAR(100)", + createValue: "'medium char'", + }, { + // CHAR 10 bits (250 * 3 = 750, 512<=750<124) + name: "longchar", + createType: "CHAR(250)", + createValue: "'long char'", + }, { + // GEOMETRY + name: "geo_stuff", + createType: "GEOMETRY", + createValue: "ST_GeomFromText('POINT(1 1)')", + }} + + conn, isMariaDB, f := connectForReplication(t, params, true /* rbr */) + defer conn.Close() + + // MariaDB timestamp(N) is not supported by our RBR. See doc.go. + if !isMariaDB { + testcases = append(testcases, []struct { + name string + createType string + createValue string + }{{ + // TIMESTAMP (second precision) + name: "timestamp_second", + createType: "TIMESTAMP", + createValue: "'2012-11-10 15:34:56'", + }, { + // TIMESTAMP (100 millisecond precision) + name: "timestamp_100millisecond", + createType: "TIMESTAMP(1)", + createValue: "'2012-11-10 15:34:56.6'", + }, { + // TIMESTAMP (10 millisecond precision) + name: "timestamp_10millisecond", + createType: "TIMESTAMP(2)", + createValue: "'2012-11-10 15:34:56.01'", + }, { + // TIMESTAMP (millisecond precision) + name: "timestamp_millisecond", + createType: "TIMESTAMP(3)", + createValue: "'2012-11-10 15:34:56.012'", + }, { + // TIMESTAMP (100 microsecond precision) + name: "timestamp_100microsecond", + createType: "TIMESTAMP(4)", + createValue: "'2012-11-10 15:34:56.0123'", + }, { + // TIMESTAMP (10 microsecond precision) + name: "timestamp_10microsecond", + createType: "TIMESTAMP(5)", + createValue: "'2012-11-10 15:34:56.01234'", + }, { + // TIMESTAMP (microsecond precision) + name: "timestamp_microsecond", + createType: "TIMESTAMP(6)", + createValue: "'2012-11-10 15:34:56.012345'", + }, { + // TIMESTAMP (0 with microsecond precision) + name: "timestamp_microsecond_z", + createType: "TIMESTAMP(6)", + createValue: "'0000-00-00 00:00:00.000000'", + }, { + // TIME + name: "time_100milli", + createType: "TIME(1)", + createValue: "'12:44:58.3'", + }, { + // TIME + name: "time_10milli", + createType: "TIME(2)", + createValue: "'412:44:58.01'", + }, { + // TIME + name: "time_milli", + createType: "TIME(3)", + createValue: "'-12:44:58.012'", + }, { + // TIME + name: "time_100micro", + createType: "TIME(4)", + createValue: "'12:44:58.0123'", + }, { + // TIME + name: "time_10micro", + createType: "TIME(5)", + createValue: "'12:44:58.01234'", + }, { + // TIME + name: "time_micro", + createType: "TIME(6)", + createValue: "'-12:44:58.012345'", + }, { + // DATETIME + name: "datetime1", + createType: "DATETIME(1)", + createValue: "'1020-08-23 12:44:58.8'", + }, { + // DATETIME + name: "datetime2", + createType: "DATETIME(2)", + createValue: "'1020-08-23 12:44:58.01'", + }, { + // DATETIME + name: "datetime3", + createType: "DATETIME(3)", + createValue: "'1020-08-23 12:44:58.012'", + }, { + // DATETIME + name: "datetime4", + createType: "DATETIME(4)", + createValue: "'1020-08-23 12:44:58.0123'", + }, { + // DATETIME + name: "datetime5", + createType: "DATETIME(5)", + createValue: "'1020-08-23 12:44:58.01234'", + }, { + // DATETIME + name: "datetime6", + createType: "DATETIME(6)", + createValue: "'1020-08-23 12:44:58.012345'", + }}...) + } + + // JSON is only supported by MySQL 5.7+ + // However the binary format is not just the text version. + // So it doesn't work as expected. + if false && strings.HasPrefix(conn.ServerVersion, "5.7") { + testcases = append(testcases, struct { + name string + createType string + createValue string + }{ + // JSON + name: "json1", + createType: "JSON", + createValue: "'{\"a\":\"b\"}'", + }) + } + + ctx := context.Background() + dConn, err := Connect(ctx, params) + if err != nil { + t.Fatal(err) + } + defer dConn.Close() + + // Set the connection time zone for execution of the + // statements to PST. That way we're sure to test the + // conversion for the TIMESTAMP types. + if _, err := dConn.ExecuteFetch("SET time_zone = '+08:00'", 0, false); err != nil { + t.Fatal(err) + } + + // Create the table with all fields. + createTable := "create table replicationtypes(id int" + for _, tcase := range testcases { + createTable += fmt.Sprintf(", %v %v", tcase.name, tcase.createType) + } + createTable += ", primary key(id))" + if _, err := dConn.ExecuteFetch(createTable, 0, false); err != nil { + t.Fatal(err) + } + + // Insert the value with all fields. + insert := "insert into replicationtypes set id=1" + for _, tcase := range testcases { + insert += fmt.Sprintf(", %v=%v", tcase.name, tcase.createValue) + } + result, err := dConn.ExecuteFetch(insert, 0, false) + if err != nil { + t.Fatalf("insert failed: %v", err) + } + if result.RowsAffected != 1 || len(result.Rows) != 0 { + t.Errorf("unexpected result for insert: %v", result) + } + + // Get the new events from the binlogs. + // Only care about the Write event. + var tableID uint64 + var tableMap *replication.TableMap + var values []sqltypes.Value + + for values == nil { + data, err := conn.ReadPacket() + if err != nil { + t.Fatalf("ReadPacket failed: %v", err) + } + + // Make sure it's a replication packet. + switch data[0] { + case OKPacket: + // What we expect, handled below. + case ErrPacket: + err := parseErrorPacket(data) + t.Fatalf("ReadPacket returned an error packet: %v", err) + default: + // Very unexpected. + t.Fatalf("ReadPacket returned a weird packet: %v", data) + } + + // See what we got, strip the checksum. + be := newBinlogEvent(isMariaDB, data) + if !be.IsValid() { + t.Fatalf("read an invalid packet: %v", be) + } + be, _, err = be.StripChecksum(f) + if err != nil { + t.Fatalf("StripChecksum failed: %v", err) + } + switch { + case be.IsTableMap(): + tableID = be.TableID(f) // This would be 0x00ffffff for an event to clear all table map entries. + var err error + tableMap, err = be.TableMap(f) + if err != nil { + t.Fatalf("TableMap event is broken: %v", err) + } + t.Logf("Got Table Map event: %v %v", tableID, tableMap) + if tableMap.Database != "vttest" || + tableMap.Name != "replicationtypes" || + len(tableMap.Types) != len(testcases)+1 || + tableMap.CanBeNull.Bit(0) { + t.Errorf("got wrong TableMap: %v", tableMap) + } + case be.IsWriteRows(): + if got := be.TableID(f); got != tableID { + t.Fatalf("WriteRows event got table ID %v but was expecting %v", got, tableID) + } + wr, err := be.Rows(f, tableMap) + if err != nil { + t.Fatalf("Rows event is broken: %v", err) + } + + // Check it has the right values + values, err = valuesForTests(t, &wr, tableMap, 0) + if err != nil { + t.Fatalf("valuesForTests is broken: %v", err) + } + t.Logf("Got WriteRows event data: %v %v", wr, values) + if len(values) != len(testcases)+1 { + t.Fatalf("Got wrong length %v for values, was expecting %v", len(values), len(testcases)+1) + } + + default: + t.Logf("Got unrelated event: %v", be) + } + } + + // Insert a second row with the same data. + var sql bytes.Buffer + sql.WriteString("insert into replicationtypes set id=2") + for i, tcase := range testcases { + sql.WriteString(", ") + sql.WriteString(tcase.name) + sql.WriteString(" = ") + if values[i+1].Type() == querypb.Type_TIMESTAMP && !bytes.HasPrefix(values[i+1].Raw(), replication.ZeroTimestamp) { + // Values in the binary log are UTC. Let's convert them + // to whatever timezone the connection is using, + // so MySQL properly converts them back to UTC. + sql.WriteString("convert_tz(") + values[i+1].EncodeSQL(&sql) + sql.WriteString(", '+00:00', @@session.time_zone)") + } else { + values[i+1].EncodeSQL(&sql) + } + } + result, err = dConn.ExecuteFetch(sql.String(), 0, false) + if err != nil { + t.Fatalf("insert '%v' failed: %v", sql.String(), err) + } + if result.RowsAffected != 1 || len(result.Rows) != 0 { + t.Errorf("unexpected result for insert: %v", result) + } + t.Logf("Insert after getting event is: %v", sql.String()) + + // Re-select both rows, make sure all columns are the same. + stmt := "select id" + for _, tcase := range testcases { + stmt += ", " + tcase.name + } + stmt += " from replicationtypes" + result, err = dConn.ExecuteFetch(stmt, 2, false) + if err != nil { + t.Fatalf("select failed: %v", err) + } + if len(result.Rows) != 2 { + t.Fatalf("unexpected result for select: %v", result) + } + for i, tcase := range testcases { + if !reflect.DeepEqual(result.Rows[0][i+1], result.Rows[1][i+1]) { + t.Errorf("Field %v is not the same, got %v(%v) and %v(%v)", tcase.name, result.Rows[0][i+1], result.Rows[0][i+1].Type, result.Rows[1][i+1], result.Rows[1][i+1].Type) + } + } + + // Drop the table, we're done. + if _, err := dConn.ExecuteFetch("drop table replicationtypes", 0, false); err != nil { + t.Fatal(err) + } + +} + +// valuesForTests is a helper method to return the sqltypes.Value +// of all columns in a row in a Row. Only use it in tests, as the +// returned values cannot be interpreted correctly without the schema. +// We assume everything is unsigned in this method. +func valuesForTests(t *testing.T, rs *replication.Rows, tm *replication.TableMap, rowIndex int) ([]sqltypes.Value, error) { + var result []sqltypes.Value + + valueIndex := 0 + data := rs.Rows[rowIndex].Data + pos := 0 + for c := 0; c < rs.DataColumns.Count(); c++ { + if !rs.DataColumns.Bit(c) { + continue + } + + if rs.Rows[rowIndex].NullColumns.Bit(valueIndex) { + // This column is represented, but its value is NULL. + result = append(result, sqltypes.NULL) + valueIndex++ + continue + } + + // We have real data + value, l, err := replication.CellValue(data, pos, tm.Types[c], tm.Metadata[c], querypb.Type_UINT64) + if err != nil { + return nil, err + } + result = append(result, value) + t.Logf(" %v: type=%v data=%v metadata=%v -> %v", c, tm.Types[c], data[pos:pos+l], tm.Metadata[c], value) + pos += l + valueIndex++ + } + + return result, nil +} diff --git a/go/mysqlconn/server.go b/go/mysqlconn/server.go index 824427cef10..805a3510d85 100644 --- a/go/mysqlconn/server.go +++ b/go/mysqlconn/server.go @@ -258,7 +258,6 @@ func (l *Listener) handle(conn net.Conn, connectionID uint32) { switch data[0] { case ComQuit: - log.Infof("Client %v wants to disconnect, closing connection.", c.ConnectionID) return case ComInitDB: db := c.parseComInitDB(data) @@ -269,7 +268,6 @@ func (l *Listener) handle(conn net.Conn, connectionID uint32) { } case ComQuery: query := c.parseComQuery(data) - log.Infof("Received command from client %v: %v", c.ConnectionID, query) result, err := l.handler.ComQuery(c, query) if err != nil { if werr := c.writeErrorPacketFromError(err); werr != nil { diff --git a/go/mysqlconn/server_test.go b/go/mysqlconn/server_test.go index abcb527e032..cd3c87c1391 100644 --- a/go/mysqlconn/server_test.go +++ b/go/mysqlconn/server_test.go @@ -264,6 +264,11 @@ func TestServer(t *testing.T) { // TestClearTextServer creates a Server that needs clear text passwords from the client. func TestClearTextServer(t *testing.T) { + // If the database we're using is MariaDB, the client + // is also the MariaDB client, that does support + // clear text by default. + isMariaDB := os.Getenv("MYSQL_FLAVOR") == "MariaDB" + th := &testHandler{} authServer := NewAuthServerConfig() @@ -292,24 +297,34 @@ func TestClearTextServer(t *testing.T) { Pass: "password1", } - // Run a 'select rows' command with results. - // This should fail as clear text is not enabled by default on the client. + // Run a 'select rows' command with results. This should fail + // as clear text is not enabled by default on the client + // (except MariaDB). l.AllowClearTextWithoutTLS = true - output, ok := runMysql(t, params, "select rows") + sql := "select rows" + output, ok := runMysql(t, params, sql) if ok { - t.Fatalf("mysql should have failed but returned: %v", output) - } - if strings.Contains(output, "No such file or directory") { - t.Logf("skipping mysql clear text tests, as the clear text plugin cannot be loaded: %v", err) - return - } - if !strings.Contains(output, "plugin not enabled") { - t.Errorf("Unexpected output for 'select rows': %v", output) + if isMariaDB { + t.Logf("mysql should have failed but returned: %v\nbut letting it go on MariaDB", output) + } else { + t.Fatalf("mysql should have failed but returned: %v", output) + } + } else { + if strings.Contains(output, "No such file or directory") { + t.Logf("skipping mysql clear text tests, as the clear text plugin cannot be loaded: %v", err) + return + } + if !strings.Contains(output, "plugin not enabled") { + t.Errorf("Unexpected output for 'select rows': %v", output) + } } // Now enable clear text plugin in client, but server requires SSL. l.AllowClearTextWithoutTLS = false - output, ok = runMysql(t, params, enableCleartextPluginPrefix+"select rows") + if !isMariaDB { + sql = enableCleartextPluginPrefix + sql + } + output, ok = runMysql(t, params, sql) if ok { t.Fatalf("mysql should have failed but returned: %v", output) } @@ -319,7 +334,7 @@ func TestClearTextServer(t *testing.T) { // Now enable clear text plugin, it should now work. l.AllowClearTextWithoutTLS = true - output, ok = runMysql(t, params, enableCleartextPluginPrefix+"select rows") + output, ok = runMysql(t, params, sql) if !ok { t.Fatalf("mysql failed: %v", output) } @@ -331,7 +346,7 @@ func TestClearTextServer(t *testing.T) { // Change password, make sure server rejects us. params.Pass = "" - output, ok = runMysql(t, params, enableCleartextPluginPrefix+"select rows") + output, ok = runMysql(t, params, sql) if ok { t.Fatalf("mysql should have failed but returned: %v", output) } diff --git a/go/sqltypes/proto3.go b/go/sqltypes/proto3.go index 146692061a5..2a0af8a3a01 100644 --- a/go/sqltypes/proto3.go +++ b/go/sqltypes/proto3.go @@ -10,6 +10,30 @@ import "github.com/youtube/vitess/go/vt/vterrors" // This file contains the proto3 conversion functions for the structures // defined here. +// RowToProto3 converts []Value to proto3. +func RowToProto3(row []Value) *querypb.Row { + result := &querypb.Row{} + result.Lengths = make([]int64, 0, len(row)) + total := 0 + for _, c := range row { + if c.IsNull() { + result.Lengths = append(result.Lengths, -1) + continue + } + length := c.Len() + result.Lengths = append(result.Lengths, int64(length)) + total += length + } + result.Values = make([]byte, 0, total) + for _, c := range row { + if c.IsNull() { + continue + } + result.Values = append(result.Values, c.Raw()...) + } + return result +} + // RowsToProto3 converts [][]Value to proto3. func RowsToProto3(rows [][]Value) []*querypb.Row { if len(rows) == 0 { @@ -18,26 +42,7 @@ func RowsToProto3(rows [][]Value) []*querypb.Row { result := make([]*querypb.Row, len(rows)) for i, r := range rows { - row := &querypb.Row{} - result[i] = row - row.Lengths = make([]int64, 0, len(r)) - total := 0 - for _, c := range r { - if c.IsNull() { - row.Lengths = append(row.Lengths, -1) - continue - } - length := c.Len() - row.Lengths = append(row.Lengths, int64(length)) - total += length - } - row.Values = make([]byte, 0, total) - for _, c := range r { - if c.IsNull() { - continue - } - row.Values = append(row.Values, c.Raw()...) - } + result[i] = RowToProto3(r) } return result } diff --git a/go/sync2/consolidator.go b/go/sync2/consolidator.go index dbcfce11dbe..52de3105c42 100644 --- a/go/sync2/consolidator.go +++ b/go/sync2/consolidator.go @@ -17,18 +17,25 @@ import ( // Consolidator consolidates duplicate queries from executing simulaneously // and shares results between them. type Consolidator struct { - mu sync.Mutex - queries map[string]*Result - consolidations *cache.LRUCache + *ConsolidatorCache + + mu sync.Mutex + queries map[string]*Result } // NewConsolidator creates a new Consolidator func NewConsolidator() *Consolidator { - return &Consolidator{queries: make(map[string]*Result), consolidations: cache.NewLRUCache(1000)} + return &Consolidator{ + queries: make(map[string]*Result), + ConsolidatorCache: NewConsolidatorCache(1000), + } } // Result is a wrapper for result of a query. type Result struct { + // executing is used to block additional requests. + // The original request holds a write lock while additional ones are blocked + // on acquiring a read lock (see Wait() below.) executing sync.RWMutex consolidator *Consolidator query string @@ -51,12 +58,43 @@ func (co *Consolidator) Create(query string) (r *Result, created bool) { return r, true } -func (co *Consolidator) ServeHTTP(response http.ResponseWriter, request *http.Request) { +// Broadcast removes the entry from current queries and releases the +// lock on its Result. Broadcast should be invoked when original +// query completes execution. +func (rs *Result) Broadcast() { + rs.consolidator.mu.Lock() + defer rs.consolidator.mu.Unlock() + delete(rs.consolidator.queries, rs.query) + rs.executing.Unlock() +} + +// Wait waits for the original query to complete execution. Wait should +// be invoked for duplicate queries. +func (rs *Result) Wait() { + rs.consolidator.Record(rs.query) + rs.executing.RLock() +} + +// ConsolidatorCache is a thread-safe object used for counting how often recent +// queries have been consolidated. +// It is also used by the txserializer package to count how often transactions +// have been queued and had to wait because they targeted the same row (range). +type ConsolidatorCache struct { + *cache.LRUCache +} + +// NewConsolidatorCache creates a new cache with the given capacity. +func NewConsolidatorCache(capacity int64) *ConsolidatorCache { + return &ConsolidatorCache{cache.NewLRUCache(capacity)} +} + +// ServeHTTP lists the most recent, cached queries and their count. +func (cc *ConsolidatorCache) ServeHTTP(response http.ResponseWriter, request *http.Request) { if err := acl.CheckAccessHTTP(request, acl.DEBUGGING); err != nil { acl.SendError(response, err) return } - items := co.consolidations.Items() + items := cc.Items() response.Header().Set("Content-Type", "text/plain") if items == nil { response.Write([]byte("empty\n")) @@ -64,50 +102,36 @@ func (co *Consolidator) ServeHTTP(response http.ResponseWriter, request *http.Re } response.Write([]byte(fmt.Sprintf("Length: %d\n", len(items)))) for _, v := range items { - response.Write([]byte(fmt.Sprintf("%v: %s\n", v.Value.(*ccount).Get(), v.Key))) + response.Write([]byte(fmt.Sprintf("%v: %s\n", v.Value.(*ccount).get(), v.Key))) } } -func (co *Consolidator) record(query string) { - if v, ok := co.consolidations.Get(query); ok { - v.(*ccount).Add(1) +// Record increments the count for "query" by 1. +// If it's not in the cache yet, it will be added. +func (cc *ConsolidatorCache) Record(query string) { + if v, ok := cc.Get(query); ok { + v.(*ccount).add(1) } else { c := ccount(1) - co.consolidations.Set(query, &c) + cc.Set(query, &c) } } -// Broadcast removes the entry from current queries and releases the -// lock on its Result. Broadcast should be invoked when original -// query completes execution. -func (rs *Result) Broadcast() { - rs.consolidator.mu.Lock() - defer rs.consolidator.mu.Unlock() - delete(rs.consolidator.queries, rs.query) - rs.executing.Unlock() -} - -// Wait waits for the original query to complete execution. Wait should -// be invoked for duplicate queries. -func (rs *Result) Wait() { - rs.consolidator.record(rs.query) - rs.executing.RLock() -} - +// ccount elements are used with a cache.LRUCache object to track if another +// request for the same query is already in progress. type ccount int64 +// Size always returns 1 because we use the cache only to track queries, +// independent of the number of requests waiting for them. +// This implements the cache.Value interface. func (cc *ccount) Size() int { return 1 } -func (cc *ccount) Add(n int64) int64 { +func (cc *ccount) add(n int64) int64 { return atomic.AddInt64((*int64)(cc), n) } -func (cc *ccount) Set(n int64) { - atomic.StoreInt64((*int64)(cc), n) -} - -func (cc *ccount) Get() int64 { +func (cc *ccount) get() int64 { return atomic.LoadInt64((*int64)(cc)) } diff --git a/go/sync2/consolidator_test.go b/go/sync2/consolidator_test.go index cc70066aaf1..d2f6cc457fc 100644 --- a/go/sync2/consolidator_test.go +++ b/go/sync2/consolidator_test.go @@ -8,12 +8,12 @@ func TestConsolidator(t *testing.T) { orig, added := con.Create(sql) if !added { - t.Errorf("expected consolidator to register a new entry") + t.Fatalf("expected consolidator to register a new entry") } dup, added := con.Create(sql) if added { - t.Errorf("did not expect consolidator to register a new entry") + t.Fatalf("did not expect consolidator to register a new entry") } result := 1 @@ -27,13 +27,13 @@ func TestConsolidator(t *testing.T) { t.Errorf("failed to pass result") } if *orig.Result.(*int) != *dup.Result.(*int) { - t.Errorf("failed to share the result") + t.Fatalf("failed to share the result") } // Running the query again should add a new entry since the original // query execution completed _, added = con.Create(sql) if !added { - t.Errorf("expected consolidator to register a new entry") + t.Fatalf("expected consolidator to register a new entry") } } diff --git a/go/vt/binlog/binlog_streamer.go b/go/vt/binlog/binlog_streamer.go index 8bc74014553..f0b0f5970f1 100644 --- a/go/vt/binlog/binlog_streamer.go +++ b/go/vt/binlog/binlog_streamer.go @@ -15,6 +15,7 @@ import ( "github.com/youtube/vitess/go/mysqlconn/replication" "github.com/youtube/vitess/go/sqldb" + "github.com/youtube/vitess/go/sqltypes" "github.com/youtube/vitess/go/stats" "github.com/youtube/vitess/go/vt/mysqlctl" "github.com/youtube/vitess/go/vt/sqlparser" @@ -60,7 +61,7 @@ type FullBinlogStatement struct { Table string KeyspaceID []byte PKNames []*querypb.Field - PKRow *querypb.Row + PKValues []sqltypes.Value } // sendTransactionFunc is used to send binlog events. @@ -75,14 +76,57 @@ func getStatementCategory(sql string) binlogdatapb.BinlogTransaction_Statement_C return statementPrefixes[strings.ToLower(sql)] } +// tableCacheEntry contains everything we know about a table. +// It is created when we get a TableMap event. +type tableCacheEntry struct { + // tm is what we get from a TableMap event. + tm *replication.TableMap + + // ti is the table descriptor we get from the schema engine. + ti *schema.Table + + // The following fields are used if we want to extract the + // keyspace_id of a row. + + // resolver is only set if Streamer.resolverFactory is set. + resolver keyspaceIDResolver + + // keyspaceIDIndex is the index of the field that can be used + // to compute the keyspaceID. Set to -1 if no resolver is in used. + keyspaceIDIndex int + + // The following fields are used if we want to extract the + // primary key of a row. + + // pkNames contains an array of fields for the PK. + pkNames []*querypb.Field + + // pkIndexes contains the index of a given column in the + // PK. It is -1 f the column is not in any PK. It contains as + // many fields as there are columns in the table. + // For instance, in a table defined like this: + // field1 varchar() + // pkpart2 int + // pkpart1 int + // pkIndexes would contain: [ + // -1 // field1 is not in the pk + // 1 // pkpart2 is the second part of the PK + // 0 // pkpart1 is the first part of the PK + // This array is built this way so when we extract the columns + // in a row, we can just save them in the PK array easily. + pkIndexes []int +} + // Streamer streams binlog events from MySQL by connecting as a slave. // A Streamer should only be used once. To start another stream, call // NewStreamer() again. type Streamer struct { // The following fields at set at creation and immutable. - dbname string - mysqld mysqlctl.MysqlDaemon - se *schema.Engine + dbname string + mysqld mysqlctl.MysqlDaemon + se *schema.Engine + resolverFactory keyspaceIDResolverFactory + extractPK bool clientCharset *binlogdatapb.Charset startPos replication.Position @@ -193,7 +237,7 @@ func (bls *Streamer) parseEvents(ctx context.Context, events <-chan replication. // Remember the RBR state. // tableMaps is indexed by tableID. - tableMaps := make(map[uint64]*replication.TableMap) + tableMaps := make(map[uint64]*tableCacheEntry) // A begin can be triggered either by a BEGIN query, or by a GTID_EVENT. begin := func() { @@ -388,21 +432,63 @@ func (bls *Streamer) parseEvents(ctx context.Context, events <-chan replication. if err != nil { return pos, err } - tableMaps[tableID] = tm + // TODO(alainjobart) if table is already in map, + // just use it. + + tce := &tableCacheEntry{ + tm: tm, + keyspaceIDIndex: -1, + } + tableMaps[tableID] = tce + + // Check we're in the right database, and if so, fill + // in more data. + if tm.Database != "" && tm.Database != bls.dbname { + continue + } + + // Find and fill in the table schema. + tce.ti = bls.se.GetTable(sqlparser.NewTableIdent(tm.Name)) + if tce.ti == nil { + return pos, fmt.Errorf("unknown table %v in schema", tm.Name) + } + + // Fill in the resolver if needed. + if bls.resolverFactory != nil { + tce.keyspaceIDIndex, tce.resolver, err = bls.resolverFactory(tce.ti) + if err != nil { + return pos, fmt.Errorf("cannot find column to use to find keyspace_id for table %v", tm.Name) + } + } + + // Fill in PK indexes if necessary. + if bls.extractPK { + tce.pkNames = make([]*querypb.Field, len(tce.ti.PKColumns)) + tce.pkIndexes = make([]int, len(tce.ti.Columns)) + for i := range tce.pkIndexes { + // Put -1 as default in here. + tce.pkIndexes[i] = -1 + } + for i, c := range tce.ti.PKColumns { + // Patch in every PK column index. + tce.pkIndexes[c] = i + // Fill in pknames + tce.pkNames[i] = &querypb.Field{ + Name: tce.ti.Columns[c].Name.String(), + Type: tce.ti.Columns[c].Type, + } + } + } case ev.IsWriteRows(): tableID := ev.TableID(format) - tm, ok := tableMaps[tableID] + tce, ok := tableMaps[tableID] if !ok { return pos, fmt.Errorf("unknown tableID %v in WriteRows event", tableID) } - if tm.Database != "" && tm.Database != bls.dbname { + if tce.ti == nil { // Skip cross-db statements. continue } - ti := bls.se.GetTable(sqlparser.NewTableIdent(tm.Name)) - if ti == nil { - return pos, fmt.Errorf("unknown table %v in schema", tm.Name) - } setTimestamp := &binlogdatapb.BinlogTransaction_Statement{ Category: binlogdatapb.BinlogTransaction_Statement_BL_SET, Sql: []byte(fmt.Sprintf("SET TIMESTAMP=%d", ev.Timestamp())), @@ -411,12 +497,12 @@ func (bls *Streamer) parseEvents(ctx context.Context, events <-chan replication. Statement: setTimestamp, }) - rows, err := ev.Rows(format, tm) + rows, err := ev.Rows(format, tce.tm) if err != nil { return pos, err } - statements = appendInserts(statements, &rows, tm, ti) + statements = bls.appendInserts(statements, tce, &rows) if autocommit { if err = commit(ev.Timestamp()); err != nil { @@ -425,18 +511,14 @@ func (bls *Streamer) parseEvents(ctx context.Context, events <-chan replication. } case ev.IsUpdateRows(): tableID := ev.TableID(format) - tm, ok := tableMaps[tableID] + tce, ok := tableMaps[tableID] if !ok { return pos, fmt.Errorf("unknown tableID %v in UpdateRows event", tableID) } - if tm.Database != "" && tm.Database != bls.dbname { + if tce.ti == nil { // Skip cross-db statements. continue } - ti := bls.se.GetTable(sqlparser.NewTableIdent(tm.Name)) - if ti == nil { - return pos, fmt.Errorf("unknown table %v in schema", tm.Name) - } setTimestamp := &binlogdatapb.BinlogTransaction_Statement{ Category: binlogdatapb.BinlogTransaction_Statement_BL_SET, Sql: []byte(fmt.Sprintf("SET TIMESTAMP=%d", ev.Timestamp())), @@ -445,12 +527,12 @@ func (bls *Streamer) parseEvents(ctx context.Context, events <-chan replication. Statement: setTimestamp, }) - rows, err := ev.Rows(format, tm) + rows, err := ev.Rows(format, tce.tm) if err != nil { return pos, err } - statements = appendUpdates(statements, &rows, tm, ti) + statements = bls.appendUpdates(statements, tce, &rows) if autocommit { if err = commit(ev.Timestamp()); err != nil { @@ -459,18 +541,14 @@ func (bls *Streamer) parseEvents(ctx context.Context, events <-chan replication. } case ev.IsDeleteRows(): tableID := ev.TableID(format) - tm, ok := tableMaps[tableID] + tce, ok := tableMaps[tableID] if !ok { return pos, fmt.Errorf("unknown tableID %v in DeleteRows event", tableID) } - if tm.Database != "" && tm.Database != bls.dbname { + if tce.ti == nil { // Skip cross-db statements. continue } - ti := bls.se.GetTable(sqlparser.NewTableIdent(tm.Name)) - if ti == nil { - return pos, fmt.Errorf("unknown table %v in schema", tm.Name) - } setTimestamp := &binlogdatapb.BinlogTransaction_Statement{ Category: binlogdatapb.BinlogTransaction_Statement_BL_SET, Sql: []byte(fmt.Sprintf("SET TIMESTAMP=%d", ev.Timestamp())), @@ -479,12 +557,12 @@ func (bls *Streamer) parseEvents(ctx context.Context, events <-chan replication. Statement: setTimestamp, }) - rows, err := ev.Rows(format, tm) + rows, err := ev.Rows(format, tce.tm) if err != nil { return pos, err } - statements = appendDeletes(statements, &rows, tm, ti) + statements = bls.appendDeletes(statements, tce, &rows) if autocommit { if err = commit(ev.Timestamp()); err != nil { @@ -495,100 +573,142 @@ func (bls *Streamer) parseEvents(ctx context.Context, events <-chan replication. } } -func appendInserts(statements []FullBinlogStatement, rows *replication.Rows, tm *replication.TableMap, ti *schema.Table) []FullBinlogStatement { +func (bls *Streamer) appendInserts(statements []FullBinlogStatement, tce *tableCacheEntry, rows *replication.Rows) []FullBinlogStatement { for i := range rows.Rows { var sql bytes.Buffer sql.WriteString("INSERT INTO ") - sql.WriteString(tm.Name) + sql.WriteString(tce.tm.Name) sql.WriteString(" SET ") - if err := writeValuesAsSQL(&sql, rows, tm, ti, i); err != nil { + keyspaceIDCell, pkValues, err := writeValuesAsSQL(&sql, tce, rows, i, tce.pkNames != nil) + if err != nil { log.Warningf("writeValuesAsSQL(%v) failed: %v", i, err) continue } + // Fill in keyspace id if needed. + var ksid []byte + if tce.resolver != nil { + var err error + ksid, err = tce.resolver.keyspaceID(keyspaceIDCell) + if err != nil { + log.Warningf("resolver(%v) failed: %v", err) + } + } + statement := &binlogdatapb.BinlogTransaction_Statement{ Category: binlogdatapb.BinlogTransaction_Statement_BL_INSERT, Sql: sql.Bytes(), } statements = append(statements, FullBinlogStatement{ - Statement: statement, - Table: tm.Name, + Statement: statement, + Table: tce.tm.Name, + KeyspaceID: ksid, + PKNames: tce.pkNames, + PKValues: pkValues, }) - // TODO(alainjobart): fill in keyspaceID, pkNames, pkRows - // if necessary. } return statements } -func appendUpdates(statements []FullBinlogStatement, rows *replication.Rows, tm *replication.TableMap, ti *schema.Table) []FullBinlogStatement { +func (bls *Streamer) appendUpdates(statements []FullBinlogStatement, tce *tableCacheEntry, rows *replication.Rows) []FullBinlogStatement { for i := range rows.Rows { var sql bytes.Buffer sql.WriteString("UPDATE ") - sql.WriteString(tm.Name) + sql.WriteString(tce.tm.Name) sql.WriteString(" SET ") - if err := writeValuesAsSQL(&sql, rows, tm, ti, i); err != nil { + keyspaceIDCell, pkValues, err := writeValuesAsSQL(&sql, tce, rows, i, tce.pkNames != nil) + if err != nil { log.Warningf("writeValuesAsSQL(%v) failed: %v", i, err) continue } sql.WriteString(" WHERE ") - if err := writeIdentifiesAsSQL(&sql, rows, tm, ti, i); err != nil { + if _, _, err := writeIdentifiesAsSQL(&sql, tce, rows, i, false); err != nil { log.Warningf("writeIdentifiesAsSQL(%v) failed: %v", i, err) continue } + // Fill in keyspace id if needed. + var ksid []byte + if tce.resolver != nil { + var err error + ksid, err = tce.resolver.keyspaceID(keyspaceIDCell) + if err != nil { + log.Warningf("resolver(%v) failed: %v", err) + } + } + update := &binlogdatapb.BinlogTransaction_Statement{ Category: binlogdatapb.BinlogTransaction_Statement_BL_UPDATE, Sql: sql.Bytes(), } statements = append(statements, FullBinlogStatement{ - Statement: update, - Table: tm.Name, + Statement: update, + Table: tce.tm.Name, + KeyspaceID: ksid, + PKNames: tce.pkNames, + PKValues: pkValues, }) - // TODO(alainjobart): fill in keyspaceID, pkNames, pkRows - // if necessary. } return statements } -func appendDeletes(statements []FullBinlogStatement, rows *replication.Rows, tm *replication.TableMap, ti *schema.Table) []FullBinlogStatement { +func (bls *Streamer) appendDeletes(statements []FullBinlogStatement, tce *tableCacheEntry, rows *replication.Rows) []FullBinlogStatement { for i := range rows.Rows { var sql bytes.Buffer sql.WriteString("DELETE FROM ") - sql.WriteString(tm.Name) + sql.WriteString(tce.tm.Name) sql.WriteString(" WHERE ") - if err := writeIdentifiesAsSQL(&sql, rows, tm, ti, i); err != nil { + keyspaceIDCell, pkValues, err := writeIdentifiesAsSQL(&sql, tce, rows, i, tce.pkNames != nil) + if err != nil { log.Warningf("writeIdentifiesAsSQL(%v) failed: %v", i, err) continue } + // Fill in keyspace id if needed. + var ksid []byte + if tce.resolver != nil { + var err error + ksid, err = tce.resolver.keyspaceID(keyspaceIDCell) + if err != nil { + log.Warningf("resolver(%v) failed: %v", err) + } + } + statement := &binlogdatapb.BinlogTransaction_Statement{ Category: binlogdatapb.BinlogTransaction_Statement_BL_DELETE, Sql: sql.Bytes(), } statements = append(statements, FullBinlogStatement{ - Statement: statement, - Table: tm.Name, + Statement: statement, + Table: tce.tm.Name, + KeyspaceID: ksid, + PKNames: tce.pkNames, + PKValues: pkValues, }) - // TODO(alainjobart): fill in keyspaceID, pkNames, pkRows - // if necessary. } return statements } // writeValuesAsSQL is a helper method to print the values as SQL in the -// provided bytes.Buffer. -func writeValuesAsSQL(sql *bytes.Buffer, rs *replication.Rows, tm *replication.TableMap, ti *schema.Table, rowIndex int) error { +// provided bytes.Buffer. It also returns the value for the keyspaceIDColumn, +// and the array of values for the PK, if necessary. +func writeValuesAsSQL(sql *bytes.Buffer, tce *tableCacheEntry, rs *replication.Rows, rowIndex int, getPK bool) (sqltypes.Value, []sqltypes.Value, error) { valueIndex := 0 data := rs.Rows[rowIndex].Data pos := 0 + var keyspaceIDCell sqltypes.Value + var pkValues []sqltypes.Value + if getPK { + pkValues = make([]sqltypes.Value, len(tce.pkNames)) + } for c := 0; c < rs.DataColumns.Count(); c++ { if !rs.DataColumns.Bit(c) { continue @@ -598,7 +718,7 @@ func writeValuesAsSQL(sql *bytes.Buffer, rs *replication.Rows, tm *replication.T if valueIndex > 0 { sql.WriteString(", ") } - sql.WriteString(ti.Columns[c].Name.String()) + sql.WriteString(tce.ti.Columns[c].Name.String()) sql.WriteByte('=') if rs.Rows[rowIndex].NullColumns.Bit(valueIndex) { @@ -608,25 +728,48 @@ func writeValuesAsSQL(sql *bytes.Buffer, rs *replication.Rows, tm *replication.T continue } - // We have real data - value, l, err := replication.CellValue(data, pos, tm.Types[c], tm.Metadata[c], ti.Columns[c].Type) + // We have real data. + value, l, err := replication.CellValue(data, pos, tce.tm.Types[c], tce.tm.Metadata[c], tce.ti.Columns[c].Type) if err != nil { - return err + return keyspaceIDCell, nil, err + } + if value.Type() == querypb.Type_TIMESTAMP && !bytes.HasPrefix(value.Raw(), replication.ZeroTimestamp) { + // Values in the binary log are UTC. Let's convert them + // to whatever timezone the connection is using, + // so MySQL properly converts them back to UTC. + sql.WriteString("convert_tz(") + value.EncodeSQL(sql) + sql.WriteString(", '+00:00', @@session.time_zone)") + } else { + value.EncodeSQL(sql) + } + if c == tce.keyspaceIDIndex { + keyspaceIDCell = value + } + if getPK { + if tce.pkIndexes[c] != -1 { + pkValues[tce.pkIndexes[c]] = value + } } - value.EncodeSQL(sql) pos += l valueIndex++ } - return nil + return keyspaceIDCell, pkValues, nil } // writeIdentifiesAsSQL is a helper method to print the identifies as SQL in the -// provided bytes.Buffer. -func writeIdentifiesAsSQL(sql *bytes.Buffer, rs *replication.Rows, tm *replication.TableMap, ti *schema.Table, rowIndex int) error { +// provided bytes.Buffer. It also returns the value for the keyspaceIDColumn, +// and the array of values for the PK, if necessary. +func writeIdentifiesAsSQL(sql *bytes.Buffer, tce *tableCacheEntry, rs *replication.Rows, rowIndex int, getPK bool) (sqltypes.Value, []sqltypes.Value, error) { valueIndex := 0 data := rs.Rows[rowIndex].Identify pos := 0 + var keyspaceIDCell sqltypes.Value + var pkValues []sqltypes.Value + if getPK { + pkValues = make([]sqltypes.Value, len(tce.pkNames)) + } for c := 0; c < rs.IdentifyColumns.Count(); c++ { if !rs.IdentifyColumns.Bit(c) { continue @@ -636,7 +779,7 @@ func writeIdentifiesAsSQL(sql *bytes.Buffer, rs *replication.Rows, tm *replicati if valueIndex > 0 { sql.WriteString(" AND ") } - sql.WriteString(ti.Columns[c].Name.String()) + sql.WriteString(tce.ti.Columns[c].Name.String()) sql.WriteByte('=') if rs.Rows[rowIndex].NullIdentifyColumns.Bit(valueIndex) { @@ -646,15 +789,32 @@ func writeIdentifiesAsSQL(sql *bytes.Buffer, rs *replication.Rows, tm *replicati continue } - // We have real data - value, l, err := replication.CellValue(data, pos, tm.Types[c], tm.Metadata[c], ti.Columns[c].Type) + // We have real data. + value, l, err := replication.CellValue(data, pos, tce.tm.Types[c], tce.tm.Metadata[c], tce.ti.Columns[c].Type) if err != nil { - return err + return keyspaceIDCell, nil, err + } + if value.Type() == querypb.Type_TIMESTAMP && !bytes.HasPrefix(value.Raw(), replication.ZeroTimestamp) { + // Values in the binary log are UTC. Let's convert them + // to whatever timezone the connection is using, + // so MySQL properly converts them back to UTC. + sql.WriteString("convert_tz(") + value.EncodeSQL(sql) + sql.WriteString(", '+00:00', @@session.time_zone)") + } else { + value.EncodeSQL(sql) + } + if c == tce.keyspaceIDIndex { + keyspaceIDCell = value + } + if getPK { + if tce.pkIndexes[c] != -1 { + pkValues[tce.pkIndexes[c]] = value + } } - value.EncodeSQL(sql) pos += l valueIndex++ } - return nil + return keyspaceIDCell, pkValues, nil } diff --git a/go/vt/binlog/event_streamer.go b/go/vt/binlog/event_streamer.go index f1da61b447e..f9737aeef8a 100644 --- a/go/vt/binlog/event_streamer.go +++ b/go/vt/binlog/event_streamer.go @@ -45,6 +45,7 @@ func NewEventStreamer(dbname string, mysqld mysqlctl.MysqlDaemon, se *schema.Eng sendEvent: sendEvent, } evs.bls = NewStreamer(dbname, mysqld, se, nil, startPos, timestamp, evs.transactionToEvent) + evs.bls.extractPK = true return evs } @@ -74,7 +75,7 @@ func (evs *EventStreamer) transactionToEvent(eventToken *querypb.EventToken, sta binlogdatapb.BinlogTransaction_Statement_BL_UPDATE, binlogdatapb.BinlogTransaction_Statement_BL_DELETE: var dmlStatement *querypb.StreamEvent_Statement - dmlStatement, insertid, err = evs.buildDMLStatement(string(stmt.Statement.Sql), insertid) + dmlStatement, insertid, err = evs.buildDMLStatement(stmt, insertid) if err != nil { dmlStatement = &querypb.StreamEvent_Statement{ Category: querypb.StreamEvent_Statement_Error, @@ -103,12 +104,30 @@ func (evs *EventStreamer) transactionToEvent(eventToken *querypb.EventToken, sta } /* -buildDMLStatement parses the tuples of the full stream comment. +buildDMLStatement recovers the PK from a FullBinlogStatement. +For RBR, the values are already in there, just need to be translated. +For SBR, parses the tuples of the full stream comment. The _stream comment is extracted into a StreamEvent.Statement. */ // Example query: insert into _table_(foo) values ('foo') /* _stream _table_ (eid id name ) (null 1 'bmFtZQ==' ); */ // the "null" value is used for auto-increment columns. -func (evs *EventStreamer) buildDMLStatement(sql string, insertid int64) (*querypb.StreamEvent_Statement, int64, error) { +func (evs *EventStreamer) buildDMLStatement(stmt FullBinlogStatement, insertid int64) (*querypb.StreamEvent_Statement, int64, error) { + // For RBR events, we know all this already, just extract it. + if stmt.PKNames != nil { + // We get an array of []sqltypes.Value, need to convert to querypb.Row. + dmlStatement := &querypb.StreamEvent_Statement{ + Category: querypb.StreamEvent_Statement_DML, + TableName: stmt.Table, + PrimaryKeyFields: stmt.PKNames, + PrimaryKeyValues: []*querypb.Row{sqltypes.RowToProto3(stmt.PKValues)}, + } + // InsertID is only needed to fill in the ID on next queries, + // but if we use RBR, it's already in the values, so just return 0. + return dmlStatement, 0, nil + } + + sql := string(stmt.Statement.Sql) + // first extract the comment commentIndex := strings.LastIndex(sql, streamCommentStart) if commentIndex == -1 { @@ -116,7 +135,7 @@ func (evs *EventStreamer) buildDMLStatement(sql string, insertid int64) (*queryp } dmlComment := sql[commentIndex+streamCommentStartLen:] - // then strat building the response + // then start building the response dmlStatement := &querypb.StreamEvent_Statement{ Category: querypb.StreamEvent_Statement_DML, } diff --git a/go/vt/binlog/keyrange_filter.go b/go/vt/binlog/keyrange_filter.go index 614700d9e2a..b203e0861eb 100644 --- a/go/vt/binlog/keyrange_filter.go +++ b/go/vt/binlog/keyrange_filter.go @@ -37,6 +37,18 @@ func KeyRangeFilterFunc(keyrange *topodatapb.KeyRange, callback func(*binlogdata case binlogdatapb.BinlogTransaction_Statement_BL_INSERT, binlogdatapb.BinlogTransaction_Statement_BL_UPDATE, binlogdatapb.BinlogTransaction_Statement_BL_DELETE: + // Handle RBR case first. + if statement.KeyspaceID != nil { + if !key.KeyRangeContains(keyrange, statement.KeyspaceID) { + // Skip keyspace ids that don't belong to the destination shard. + continue + } + filtered = append(filtered, statement.Statement) + matched = true + continue + } + + // SBR case. keyspaceIDS, err := sqlannotation.ExtractKeyspaceIDS(string(statement.Statement.Sql)) if err != nil { if statement.Statement.Category == binlogdatapb.BinlogTransaction_Statement_BL_INSERT { diff --git a/go/vt/binlog/keyspace_id_resolver.go b/go/vt/binlog/keyspace_id_resolver.go new file mode 100644 index 00000000000..4f71f3c3c61 --- /dev/null +++ b/go/vt/binlog/keyspace_id_resolver.go @@ -0,0 +1,164 @@ +package binlog + +import ( + "flag" + "fmt" + + "golang.org/x/net/context" + + "github.com/youtube/vitess/go/sqltypes" + "github.com/youtube/vitess/go/vt/key" + "github.com/youtube/vitess/go/vt/topo" + "github.com/youtube/vitess/go/vt/vtgate/vindexes" + "github.com/youtube/vitess/go/vt/vttablet/tabletserver/schema" + + topodatapb "github.com/youtube/vitess/go/vt/proto/topodata" +) + +var useV3ReshardingMode = flag.Bool("binlog_use_v3_resharding_mode", false, "True iff the binlog streamer should use V3-style sharding, which doesn't require a preset sharding key column.") + +// keyspaceIDResolver is constructed for a tableMap entry in RBR. It +// is used for each row, and passed in the value used for figuring out +// the keyspace id. +type keyspaceIDResolver interface { + // keyspaceID takes a table row, and returns the keyspace id as bytes. + // It will return an error if no sharding key can be found. + // The bitmap describes which columns are present in the row. + keyspaceID(value sqltypes.Value) ([]byte, error) +} + +// keyspaceIDResolverFactory creates a keyspaceIDResolver for a table +// given its schema. It returns the index of the field to used to compute +// the keyspaceID, and a function that given a value for that +// field, returns the keyspace id. +type keyspaceIDResolverFactory func(*schema.Table) (int, keyspaceIDResolver, error) + +// newKeyspaceIDResolverFactory creates a new +// keyspaceIDResolverFactory for the provided keyspace and cell. +func newKeyspaceIDResolverFactory(ctx context.Context, ts topo.Server, keyspace string, cell string) (keyspaceIDResolverFactory, error) { + if *useV3ReshardingMode { + return newKeyspaceIDResolverFactoryV3(ctx, ts, keyspace, cell) + } + + return newKeyspaceIDResolverFactoryV2(ctx, ts, keyspace) +} + +// newKeyspaceIDResolverFactoryV2 finds the ShardingColumnName / Type +// from the keyspace, and uses it to find the column name. +func newKeyspaceIDResolverFactoryV2(ctx context.Context, ts topo.Server, keyspace string) (keyspaceIDResolverFactory, error) { + ki, err := ts.GetKeyspace(ctx, keyspace) + if err != nil { + return nil, err + } + if ki.ShardingColumnName == "" { + return nil, fmt.Errorf("ShardingColumnName needs to be set for a v2 sharding key for keyspace %v", keyspace) + } + switch ki.ShardingColumnType { + case topodatapb.KeyspaceIdType_UNSET: + return nil, fmt.Errorf("ShardingColumnType needs to be set for a v2 sharding key for keyspace %v", keyspace) + case topodatapb.KeyspaceIdType_BYTES, topodatapb.KeyspaceIdType_UINT64: + // Supported values, we're good. + default: + return nil, fmt.Errorf("unknown ShardingColumnType %v for v2 sharding key for keyspace %v", ki.ShardingColumnType, keyspace) + } + return func(table *schema.Table) (int, keyspaceIDResolver, error) { + for i, col := range table.Columns { + if col.Name.EqualString(ki.ShardingColumnName) { + // We found the column. + return i, &keyspaceIDResolverFactoryV2{ + shardingColumnType: ki.ShardingColumnType, + }, nil + } + } + // The column was not found. + return -1, nil, fmt.Errorf("cannot find column %v in table %v", ki.ShardingColumnName, table.Name) + }, nil +} + +// keyspaceIDResolverFactoryV2 uses the KeyspaceInfo of the Keyspace +// to find the sharding column name. +type keyspaceIDResolverFactoryV2 struct { + shardingColumnType topodatapb.KeyspaceIdType +} + +func (r *keyspaceIDResolverFactoryV2) keyspaceID(v sqltypes.Value) ([]byte, error) { + switch r.shardingColumnType { + case topodatapb.KeyspaceIdType_BYTES: + return v.Raw(), nil + case topodatapb.KeyspaceIdType_UINT64: + i, err := v.ParseUint64() + if err != nil { + return nil, fmt.Errorf("Non numerical value: %v", err) + } + return key.Uint64Key(i).Bytes(), nil + default: + panic("unreachable") + } +} + +// newKeyspaceIDResolverFactoryV3 finds the SrvVSchema in the cell, +// gets the keyspace part, and uses it to find the column name. +func newKeyspaceIDResolverFactoryV3(ctx context.Context, ts topo.Server, keyspace string, cell string) (keyspaceIDResolverFactory, error) { + srvVSchema, err := ts.GetSrvVSchema(ctx, cell) + if err != nil { + return nil, err + } + kschema, ok := srvVSchema.Keyspaces[keyspace] + if !ok { + return nil, fmt.Errorf("SrvVSchema has no entry for keyspace %v", keyspace) + } + keyspaceSchema, err := vindexes.BuildKeyspaceSchema(kschema, keyspace) + if err != nil { + return nil, fmt.Errorf("cannot build vschema for keyspace %v: %v", keyspace, err) + } + return func(table *schema.Table) (int, keyspaceIDResolver, error) { + // Find the v3 schema. + tableSchema, ok := keyspaceSchema.Tables[table.Name.String()] + if !ok { + return -1, nil, fmt.Errorf("no vschema definition for table %v", table.Name) + } + + // The primary vindex is most likely the sharding key, + // and has to be unique. + if len(tableSchema.ColumnVindexes) == 0 { + return -1, nil, fmt.Errorf("no vindex definition for table %v", table.Name) + } + colVindex := tableSchema.ColumnVindexes[0] + if colVindex.Vindex.Cost() > 1 { + return -1, nil, fmt.Errorf("primary vindex cost is too high for table %v", table.Name) + } + unique, ok := colVindex.Vindex.(vindexes.Unique) + if !ok { + return -1, nil, fmt.Errorf("primary vindex is not unique for table %v", table.Name) + } + + shardingColumnName := colVindex.Column.String() + for i, col := range table.Columns { + if col.Name.EqualString(shardingColumnName) { + // We found the column. + return i, &keyspaceIDResolverFactoryV3{ + vindex: unique, + }, nil + } + } + // The column was not found. + return -1, nil, fmt.Errorf("cannot find column %v in table %v", shardingColumnName, table.Name) + }, nil +} + +// keyspaceIDResolverFactoryV3 uses the Vindex to compute the value. +type keyspaceIDResolverFactoryV3 struct { + vindex vindexes.Unique +} + +func (r *keyspaceIDResolverFactoryV3) keyspaceID(v sqltypes.Value) ([]byte, error) { + ids := []interface{}{v} + ksids, err := r.vindex.Map(nil, ids) + if err != nil { + return nil, err + } + if len(ksids) != 1 { + return nil, fmt.Errorf("maping row to keyspace id returned an invalid array of keyspace ids: %v", ksids) + } + return ksids[0], nil +} diff --git a/go/vt/binlog/updatestreamctl.go b/go/vt/binlog/updatestreamctl.go index 11eddf9935c..6e191531003 100644 --- a/go/vt/binlog/updatestreamctl.go +++ b/go/vt/binlog/updatestreamctl.go @@ -16,6 +16,7 @@ import ( "github.com/youtube/vitess/go/sync2" "github.com/youtube/vitess/go/tb" "github.com/youtube/vitess/go/vt/mysqlctl" + "github.com/youtube/vitess/go/vt/topo" "github.com/youtube/vitess/go/vt/vttablet/tabletserver/schema" binlogdatapb "github.com/youtube/vitess/go/vt/proto/binlogdata" @@ -93,9 +94,12 @@ func (m *UpdateStreamControlMock) IsEnabled() bool { // and UpdateStreamControl type UpdateStreamImpl struct { // the following variables are set at construction time - mysqld mysqlctl.MysqlDaemon - dbname string - se *schema.Engine + ts topo.Server + keyspace string + cell string + mysqld mysqlctl.MysqlDaemon + dbname string + se *schema.Engine // actionLock protects the following variables actionLock sync.Mutex @@ -155,11 +159,14 @@ type RegisterUpdateStreamServiceFunc func(UpdateStream) var RegisterUpdateStreamServices []RegisterUpdateStreamServiceFunc // NewUpdateStream returns a new UpdateStreamImpl object -func NewUpdateStream(mysqld mysqlctl.MysqlDaemon, se *schema.Engine, dbname string) *UpdateStreamImpl { +func NewUpdateStream(ts topo.Server, keyspace string, cell string, mysqld mysqlctl.MysqlDaemon, se *schema.Engine, dbname string) *UpdateStreamImpl { return &UpdateStreamImpl{ - mysqld: mysqld, - se: se, - dbname: dbname, + ts: ts, + keyspace: keyspace, + cell: cell, + mysqld: mysqld, + se: se, + dbname: dbname, } } @@ -245,6 +252,10 @@ func (updateStream *UpdateStreamImpl) StreamKeyRange(ctx context.Context, positi return callback(trans) }) bls := NewStreamer(updateStream.dbname, updateStream.mysqld, updateStream.se, charset, pos, 0, f) + bls.resolverFactory, err = newKeyspaceIDResolverFactory(ctx, updateStream.ts, updateStream.keyspace, updateStream.cell) + if err != nil { + return fmt.Errorf("newKeyspaceIDResolverFactory failed: %v", err) + } streamCtx, cancel := context.WithCancel(ctx) i := updateStream.streams.Add(cancel) diff --git a/go/vt/discovery/healthcheck.go b/go/vt/discovery/healthcheck.go index 0255594cd74..38d070ae801 100644 --- a/go/vt/discovery/healthcheck.go +++ b/go/vt/discovery/healthcheck.go @@ -29,6 +29,7 @@ import ( "time" log "github.com/golang/glog" + "github.com/golang/protobuf/proto" "github.com/youtube/vitess/go/netutil" "github.com/youtube/vitess/go/stats" querypb "github.com/youtube/vitess/go/vt/proto/query" @@ -148,6 +149,21 @@ func (e *TabletStats) String() string { return fmt.Sprint(*e) } +// DeepEqual compares two TabletStats. Since we include protos, we +// need to use proto.Equal on these. +func (e *TabletStats) DeepEqual(f *TabletStats) bool { + return e.Key == f.Key && + proto.Equal(e.Tablet, f.Tablet) && + e.Name == f.Name && + proto.Equal(e.Target, f.Target) && + e.Up == f.Up && + e.Serving == f.Serving && + e.TabletExternallyReparentedTimestamp == f.TabletExternallyReparentedTimestamp && + proto.Equal(e.Stats, f.Stats) && + ((e.LastError == nil && f.LastError == nil) || + (e.LastError != nil && f.LastError != nil && e.LastError.Error() == f.LastError.Error())) +} + // HealthCheck defines the interface of health checking module. // The goal of this object is to maintain a StreamHealth RPC // to a lot of tablets. Tablets are added / removed by calling the diff --git a/go/vt/discovery/replicationlag_test.go b/go/vt/discovery/replicationlag_test.go index 64d2f442b32..27a5ca52a6f 100644 --- a/go/vt/discovery/replicationlag_test.go +++ b/go/vt/discovery/replicationlag_test.go @@ -1,7 +1,6 @@ package discovery import ( - "reflect" "testing" querypb "github.com/youtube/vitess/go/vt/proto/query" @@ -29,7 +28,7 @@ func TestFilterByReplicationLag(t *testing.T) { if len(got) != 1 { t.Errorf("len(FilterByReplicationLag([{Tablet: {Uid: 1}, Serving: true}, {Tablet: {Uid: 2}, Serving: false}])) = %v, want 1", len(got)) } - if len(got) > 0 && !reflect.DeepEqual(got[0], ts1) { + if len(got) > 0 && !got[0].DeepEqual(ts1) { t.Errorf("FilterByReplicationLag([{Tablet: {Uid: 1}, Serving: true}, {Tablet: {Uid: 2}, Serving: false}]) = %+v, want %+v", got[0], ts1) } // lags of (1s, 1s, 1s, 30s) @@ -54,7 +53,7 @@ func TestFilterByReplicationLag(t *testing.T) { Stats: &querypb.RealtimeStats{SecondsBehindMaster: 30}, } got = FilterByReplicationLag([]*TabletStats{ts1, ts2, ts3, ts4}) - if len(got) != 4 || !reflect.DeepEqual(got[0], ts1) || !reflect.DeepEqual(got[1], ts2) || !reflect.DeepEqual(got[2], ts3) || !reflect.DeepEqual(got[3], ts4) { + if len(got) != 4 || !got[0].DeepEqual(ts1) || !got[1].DeepEqual(ts2) || !got[2].DeepEqual(ts3) || !got[3].DeepEqual(ts4) { t.Errorf("FilterByReplicationLag([1s, 1s, 1s, 30s]) = %+v, want all", got) } // lags of (5s, 10s, 15s, 120s) @@ -79,7 +78,7 @@ func TestFilterByReplicationLag(t *testing.T) { Stats: &querypb.RealtimeStats{SecondsBehindMaster: 120}, } got = FilterByReplicationLag([]*TabletStats{ts1, ts2, ts3, ts4}) - if len(got) != 3 || !reflect.DeepEqual(got[0], ts1) || !reflect.DeepEqual(got[1], ts2) || !reflect.DeepEqual(got[2], ts3) { + if len(got) != 3 || !got[0].DeepEqual(got[0]) || !got[1].DeepEqual(ts2) || !got[2].DeepEqual(ts3) { t.Errorf("FilterByReplicationLag([5s, 10s, 15s, 120s]) = %+v, want [5s, 10s, 15s]", got) } // lags of (30m, 35m, 40m, 45m) @@ -104,7 +103,7 @@ func TestFilterByReplicationLag(t *testing.T) { Stats: &querypb.RealtimeStats{SecondsBehindMaster: 45 * 60}, } got = FilterByReplicationLag([]*TabletStats{ts1, ts2, ts3, ts4}) - if len(got) != 4 || !reflect.DeepEqual(got[0], ts1) || !reflect.DeepEqual(got[1], ts2) || !reflect.DeepEqual(got[2], ts3) || !reflect.DeepEqual(got[3], ts4) { + if len(got) != 4 || !got[0].DeepEqual(ts1) || !got[1].DeepEqual(ts2) || !got[2].DeepEqual(ts3) || !got[3].DeepEqual(ts4) { t.Errorf("FilterByReplicationLag([30m, 35m, 40m, 45m]) = %+v, want all", got) } // lags of (1s, 1s, 1m, 40m, 40m) - not run filter the second time as first run removed two items. @@ -134,7 +133,7 @@ func TestFilterByReplicationLag(t *testing.T) { Stats: &querypb.RealtimeStats{SecondsBehindMaster: 40 * 60}, } got = FilterByReplicationLag([]*TabletStats{ts1, ts2, ts3, ts4, ts5}) - if len(got) != 3 || !reflect.DeepEqual(got[0], ts1) || !reflect.DeepEqual(got[1], ts2) || !reflect.DeepEqual(got[2], ts3) { + if len(got) != 3 || !got[0].DeepEqual(ts1) || !got[1].DeepEqual(ts2) || !got[2].DeepEqual(ts3) { t.Errorf("FilterByReplicationLag([1s, 1s, 1m, 40m, 40m]) = %+v, want [1s, 1s, 1m]", got) } // lags of (1s, 1s, 10m, 40m) - run filter twice to remove two items @@ -159,7 +158,7 @@ func TestFilterByReplicationLag(t *testing.T) { Stats: &querypb.RealtimeStats{SecondsBehindMaster: 40 * 60}, } got = FilterByReplicationLag([]*TabletStats{ts1, ts2, ts3, ts4}) - if len(got) != 2 || !reflect.DeepEqual(got[0], ts1) || !reflect.DeepEqual(got[1], ts2) { + if len(got) != 2 || !got[0].DeepEqual(ts1) || !got[1].DeepEqual(ts2) { t.Errorf("FilterByReplicationLag([1s, 1s, 10m, 40m]) = %+v, want [1s, 1s]", got) } // lags of (1m, 100m) - return at least 2 items to avoid overloading if the 2nd one is not delayed too much @@ -174,7 +173,7 @@ func TestFilterByReplicationLag(t *testing.T) { Stats: &querypb.RealtimeStats{SecondsBehindMaster: 100 * 60}, } got = FilterByReplicationLag([]*TabletStats{ts1, ts2}) - if len(got) != 2 || !reflect.DeepEqual(got[0], ts1) || !reflect.DeepEqual(got[1], ts2) { + if len(got) != 2 || !got[0].DeepEqual(ts1) || !got[1].DeepEqual(ts2) { t.Errorf("FilterByReplicationLag([1m, 100m]) = %+v, want all", got) } // lags of (1m, 3h) - return 1 if the 2nd one is delayed too much @@ -189,7 +188,7 @@ func TestFilterByReplicationLag(t *testing.T) { Stats: &querypb.RealtimeStats{SecondsBehindMaster: 3 * 60 * 60}, } got = FilterByReplicationLag([]*TabletStats{ts1, ts2}) - if len(got) != 1 || !reflect.DeepEqual(got[0], ts1) { + if len(got) != 1 || !got[0].DeepEqual(ts1) { t.Errorf("FilterByReplicationLag([1m, 3h]) = %+v, want [1m]", got) } } diff --git a/go/vt/discovery/tablet_stats_cache_test.go b/go/vt/discovery/tablet_stats_cache_test.go index 9644e2b9edb..b387e83b0b5 100644 --- a/go/vt/discovery/tablet_stats_cache_test.go +++ b/go/vt/discovery/tablet_stats_cache_test.go @@ -1,7 +1,6 @@ package discovery import ( - "reflect" "testing" "github.com/youtube/vitess/go/vt/topo" @@ -40,11 +39,11 @@ func TestTabletStatsCache(t *testing.T) { // check it's there a = tsc.GetTabletStats("k", "s", topodatapb.TabletType_REPLICA) - if len(a) != 1 || !reflect.DeepEqual(*ts1, a[0]) { + if len(a) != 1 || !ts1.DeepEqual(&a[0]) { t.Errorf("unexpected result: %v", a) } a = tsc.GetHealthyTabletStats("k", "s", topodatapb.TabletType_REPLICA) - if len(a) != 1 || !reflect.DeepEqual(*ts1, a[0]) { + if len(a) != 1 || !ts1.DeepEqual(&a[0]) { t.Errorf("unexpected result: %v", a) } @@ -61,11 +60,11 @@ func TestTabletStatsCache(t *testing.T) { // check the previous ts1 is still there, as the new one is ignored. a = tsc.GetTabletStats("k", "s", topodatapb.TabletType_REPLICA) - if len(a) != 1 || !reflect.DeepEqual(*ts1, a[0]) { + if len(a) != 1 || !ts1.DeepEqual(&a[0]) { t.Errorf("unexpected result: %v", a) } a = tsc.GetHealthyTabletStats("k", "s", topodatapb.TabletType_REPLICA) - if len(a) != 1 || !reflect.DeepEqual(*ts1, a[0]) { + if len(a) != 1 || !ts1.DeepEqual(&a[0]) { t.Errorf("unexpected result: %v", a) } @@ -82,11 +81,11 @@ func TestTabletStatsCache(t *testing.T) { // check it's there a = tsc.GetTabletStats("k", "s", topodatapb.TabletType_REPLICA) - if len(a) != 1 || !reflect.DeepEqual(*notHealthyTs1, a[0]) { + if len(a) != 1 || !notHealthyTs1.DeepEqual(&a[0]) { t.Errorf("unexpected result: %v", a) } a = tsc.GetHealthyTabletStats("k", "s", topodatapb.TabletType_REPLICA) - if len(a) != 1 || !reflect.DeepEqual(*notHealthyTs1, a[0]) { + if len(a) != 1 || !notHealthyTs1.DeepEqual(&a[0]) { t.Errorf("unexpected result: %v", a) } @@ -110,7 +109,7 @@ func TestTabletStatsCache(t *testing.T) { if a[0].Tablet.Alias.Uid == 11 { a[0], a[1] = a[1], a[0] } - if !reflect.DeepEqual(*ts1, a[0]) || !reflect.DeepEqual(*ts2, a[1]) { + if !ts1.DeepEqual(&a[0]) || !ts2.DeepEqual(&a[1]) { t.Errorf("unexpected result: %v", a) } } @@ -121,7 +120,7 @@ func TestTabletStatsCache(t *testing.T) { if a[0].Tablet.Alias.Uid == 11 { a[0], a[1] = a[1], a[0] } - if !reflect.DeepEqual(*ts1, a[0]) || !reflect.DeepEqual(*ts2, a[1]) { + if !ts1.DeepEqual(&a[0]) || !ts2.DeepEqual(&a[1]) { t.Errorf("unexpected result: %v", a) } } @@ -138,12 +137,12 @@ func TestTabletStatsCache(t *testing.T) { if a[0].Tablet.Alias.Uid == 11 { a[0], a[1] = a[1], a[0] } - if !reflect.DeepEqual(*ts1, a[0]) || !reflect.DeepEqual(*ts2, a[1]) { + if !ts1.DeepEqual(&a[0]) || !ts2.DeepEqual(&a[1]) { t.Errorf("unexpected result: %v", a) } } a = tsc.GetHealthyTabletStats("k", "s", topodatapb.TabletType_REPLICA) - if len(a) != 1 || !reflect.DeepEqual(*ts1, a[0]) { + if len(a) != 1 || !ts1.DeepEqual(&a[0]) { t.Errorf("unexpected result: %v", a) } @@ -158,13 +157,13 @@ func TestTabletStatsCache(t *testing.T) { // check we only have one replica left a = tsc.GetTabletStats("k", "s", topodatapb.TabletType_REPLICA) - if len(a) != 1 || !reflect.DeepEqual(*ts1, a[0]) { + if len(a) != 1 || !ts1.DeepEqual(&a[0]) { t.Errorf("unexpected result: %v", a) } // check we have a master now a = tsc.GetTabletStats("k", "s", topodatapb.TabletType_MASTER) - if len(a) != 1 || !reflect.DeepEqual(*ts2, a[0]) { + if len(a) != 1 || !ts2.DeepEqual(&a[0]) { t.Errorf("unexpected result: %v", a) } @@ -182,14 +181,14 @@ func TestTabletStatsCache(t *testing.T) { t.Errorf("unexpected result: %v", a) } a = tsc.GetHealthyTabletStats("k", "s", topodatapb.TabletType_MASTER) - if len(a) != 1 || !reflect.DeepEqual(*ts1, a[0]) { + if len(a) != 1 || !ts1.DeepEqual(&a[0]) { t.Errorf("unexpected result: %v", a) } // old master sending an old ping should be ignored tsc.StatsUpdate(ts2) a = tsc.GetHealthyTabletStats("k", "s", topodatapb.TabletType_MASTER) - if len(a) != 1 || !reflect.DeepEqual(*ts1, a[0]) { + if len(a) != 1 || !ts1.DeepEqual(&a[0]) { t.Errorf("unexpected result: %v", a) } } diff --git a/go/vt/discovery/utils_test.go b/go/vt/discovery/utils_test.go index fe60b6795b4..41585cb849b 100644 --- a/go/vt/discovery/utils_test.go +++ b/go/vt/discovery/utils_test.go @@ -1,7 +1,6 @@ package discovery import ( - "reflect" "testing" querypb "github.com/youtube/vitess/go/vt/proto/query" @@ -47,8 +46,15 @@ func TestRemoveUnhealthyTablets(t *testing.T) { } for _, tc := range testcases { - if got := RemoveUnhealthyTablets(tc.input); !reflect.DeepEqual(got, tc.want) { + got := RemoveUnhealthyTablets(tc.input) + if len(got) != len(tc.want) { t.Errorf("test case '%v' failed: RemoveUnhealthyTablets(%v) = %#v, want: %#v", tc.desc, tc.input, got, tc.want) + } else { + for i := range tc.want { + if !got[i].DeepEqual(&tc.want[i]) { + t.Errorf("test case '%v' failed: RemoveUnhealthyTablets(%v) = %#v, want: %#v", tc.desc, tc.input, got, tc.want) + } + } } } } diff --git a/go/vt/mysqlctl/backup.go b/go/vt/mysqlctl/backup.go index 8d3934fbaad..41a39e1661c 100644 --- a/go/vt/mysqlctl/backup.go +++ b/go/vt/mysqlctl/backup.go @@ -507,7 +507,7 @@ func backupFile(ctx context.Context, mysqld MysqlDaemon, logger logutil.Logger, // checkNoDB makes sure there is no user data already there. // Used by Restore, as we do not want to destroy an existing DB. // The user's database name must be given since we ignore all others. -// Returns true iff the specified DB either doesn't exist, or has no tables. +// Returns true if the specified DB either doesn't exist, or has no tables. // Returns (false, nil) if the check succeeds but the condition is not // satisfied (there is a DB with tables). // Returns non-nil error if one occurs while trying to perform the check. diff --git a/go/vt/proto/automation/automation.pb.go b/go/vt/proto/automation/automation.pb.go index 38dbf1e67c9..4bfbed03537 100644 --- a/go/vt/proto/automation/automation.pb.go +++ b/go/vt/proto/automation/automation.pb.go @@ -349,7 +349,7 @@ func init() { proto.RegisterFile("automation.proto", fileDescriptor0) } var fileDescriptor0 = []byte{ // 562 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x09, 0x6e, 0x88, 0x02, 0xff, 0xa4, 0x94, 0xdb, 0x6a, 0xdb, 0x4c, + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xa4, 0x94, 0xdb, 0x6a, 0xdb, 0x4c, 0x14, 0x85, 0x7f, 0xc9, 0x87, 0xbf, 0xde, 0x6a, 0x1c, 0x31, 0x34, 0x46, 0x09, 0x4d, 0x23, 0xab, 0x37, 0x26, 0x05, 0x43, 0x9d, 0x8b, 0x94, 0xb4, 0x85, 0x1a, 0x5b, 0x84, 0xe0, 0x22, 0x85, 0xb1, 0x4c, 0xa1, 0xbd, 0x30, 0x53, 0x67, 0x2e, 0x54, 0xcb, 0x92, 0x32, 0x33, 0x2a, 0xf8, 0x05, 0xfa, diff --git a/go/vt/proto/automationservice/automationservice.pb.go b/go/vt/proto/automationservice/automationservice.pb.go index 08be3dbb684..d856f86f0dc 100644 --- a/go/vt/proto/automationservice/automationservice.pb.go +++ b/go/vt/proto/automationservice/automationservice.pb.go @@ -148,7 +148,7 @@ func init() { proto.RegisterFile("automationservice.proto", fileDescriptor0) } var fileDescriptor0 = []byte{ // 150 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x09, 0x6e, 0x88, 0x02, 0xff, 0xe2, 0x12, 0x4f, 0x2c, 0x2d, 0xc9, + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0x12, 0x4f, 0x2c, 0x2d, 0xc9, 0xcf, 0x4d, 0x2c, 0xc9, 0xcc, 0xcf, 0x2b, 0x4e, 0x2d, 0x2a, 0xcb, 0x4c, 0x4e, 0xd5, 0x2b, 0x28, 0xca, 0x2f, 0xc9, 0x17, 0x12, 0xc4, 0x90, 0x90, 0x12, 0x40, 0x08, 0x41, 0x14, 0x19, 0x35, 0x32, 0x71, 0x71, 0x39, 0xc2, 0x05, 0x85, 0x4a, 0xb8, 0xc4, 0x5d, 0xf3, 0x0a, 0x4b, 0x53, 0x4b, 0x53, diff --git a/go/vt/proto/binlogdata/binlogdata.pb.go b/go/vt/proto/binlogdata/binlogdata.pb.go index c9cbcf14543..d4785aa6c95 100644 --- a/go/vt/proto/binlogdata/binlogdata.pb.go +++ b/go/vt/proto/binlogdata/binlogdata.pb.go @@ -303,7 +303,7 @@ func init() { proto.RegisterFile("binlogdata.proto", fileDescriptor0) } var fileDescriptor0 = []byte{ // 540 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x09, 0x6e, 0x88, 0x02, 0xff, 0xb4, 0x54, 0x5d, 0x6e, 0xda, 0x40, + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xb4, 0x54, 0x5d, 0x6e, 0xda, 0x40, 0x10, 0xae, 0xb1, 0x43, 0xec, 0x71, 0x9a, 0x2c, 0x9b, 0x26, 0xb2, 0x90, 0x2a, 0x21, 0xbf, 0x94, 0x97, 0xba, 0x95, 0x7b, 0x02, 0x6c, 0xaf, 0x10, 0xc9, 0x02, 0xd1, 0xe2, 0xbc, 0xf4, 0xc5, 0x32, 0x64, 0x4b, 0x11, 0xc4, 0x06, 0xef, 0x26, 0x2a, 0xe7, 0xe8, 0x29, 0x7a, 0x91, 0xde, 0xa4, 0xf7, diff --git a/go/vt/proto/binlogservice/binlogservice.pb.go b/go/vt/proto/binlogservice/binlogservice.pb.go index 451617a6f5d..b5eac227ece 100644 --- a/go/vt/proto/binlogservice/binlogservice.pb.go +++ b/go/vt/proto/binlogservice/binlogservice.pb.go @@ -204,7 +204,7 @@ func init() { proto.RegisterFile("binlogservice.proto", fileDescriptor0) } var fileDescriptor0 = []byte{ // 149 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x09, 0x6e, 0x88, 0x02, 0xff, 0xe2, 0x12, 0x4e, 0xca, 0xcc, 0xcb, + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0x12, 0x4e, 0xca, 0xcc, 0xcb, 0xc9, 0x4f, 0x2f, 0x4e, 0x2d, 0x2a, 0xcb, 0x4c, 0x4e, 0xd5, 0x2b, 0x28, 0xca, 0x2f, 0xc9, 0x17, 0xe2, 0x45, 0x11, 0x94, 0x12, 0x80, 0x70, 0x53, 0x12, 0x4b, 0x12, 0x21, 0x0a, 0x8c, 0x0e, 0x31, 0x72, 0xf1, 0x84, 0x16, 0xa4, 0x24, 0x96, 0xa4, 0x06, 0x97, 0x14, 0xa5, 0x26, 0xe6, 0x0a, 0x45, diff --git a/go/vt/proto/logutil/logutil.pb.go b/go/vt/proto/logutil/logutil.pb.go index 98596199609..93595dbcf05 100644 --- a/go/vt/proto/logutil/logutil.pb.go +++ b/go/vt/proto/logutil/logutil.pb.go @@ -146,7 +146,7 @@ func init() { proto.RegisterFile("logutil.proto", fileDescriptor0) } var fileDescriptor0 = []byte{ // 235 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x09, 0x6e, 0x88, 0x02, 0xff, 0x4c, 0x90, 0x41, 0x4b, 0xc3, 0x40, + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x4c, 0x90, 0x41, 0x4b, 0xc3, 0x40, 0x10, 0x85, 0xdd, 0x66, 0xd7, 0xd8, 0x09, 0x2d, 0x61, 0xf0, 0xb0, 0xc7, 0x58, 0x3c, 0x04, 0x0f, 0x3d, 0x54, 0xf0, 0xae, 0x12, 0xa5, 0x50, 0x12, 0x18, 0x05, 0xcf, 0x55, 0x47, 0x59, 0xd8, 0xee, 0x8a, 0x4d, 0xf3, 0x33, 0xfc, 0xcd, 0x92, 0x89, 0x91, 0xde, 0xe6, 0x7d, 0xef, 0xf1, 0xde, 0xb2, diff --git a/go/vt/proto/mysqlctl/mysqlctl.pb.go b/go/vt/proto/mysqlctl/mysqlctl.pb.go index 68ea55ec20a..e3f2beadb0e 100644 --- a/go/vt/proto/mysqlctl/mysqlctl.pb.go +++ b/go/vt/proto/mysqlctl/mysqlctl.pb.go @@ -306,7 +306,7 @@ func init() { proto.RegisterFile("mysqlctl.proto", fileDescriptor0) } var fileDescriptor0 = []byte{ // 289 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x09, 0x6e, 0x88, 0x02, 0xff, 0x7c, 0x92, 0xc1, 0x4b, 0xfb, 0x30, + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x7c, 0x92, 0xc1, 0x4b, 0xfb, 0x30, 0x1c, 0xc5, 0x7f, 0xfd, 0x89, 0x52, 0xbf, 0x6e, 0x56, 0xa2, 0x76, 0x5d, 0x41, 0xad, 0x39, 0xc8, 0x4e, 0x13, 0xf4, 0xa4, 0x37, 0x29, 0x78, 0x13, 0x21, 0x43, 0xf0, 0x56, 0xaa, 0xcd, 0x6a, 0xa1, 0x26, 0x5d, 0x92, 0x32, 0xfc, 0xc7, 0xfc, 0xfb, 0xc4, 0x34, 0xe9, 0x3a, 0x3b, 0x3d, 0xf6, 0x7d, diff --git a/go/vt/proto/query/query.pb.go b/go/vt/proto/query/query.pb.go index bc54e6429a9..82c51b6ea8f 100644 --- a/go/vt/proto/query/query.pb.go +++ b/go/vt/proto/query/query.pb.go @@ -2601,7 +2601,7 @@ func init() { proto.RegisterFile("query.proto", fileDescriptor0) } var fileDescriptor0 = []byte{ // 2797 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x09, 0x6e, 0x88, 0x02, 0xff, 0xec, 0x3a, 0x49, 0x73, 0x1b, 0xc7, + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xec, 0x3a, 0x49, 0x73, 0x1b, 0xc7, 0xd5, 0x1a, 0x2c, 0x24, 0xf0, 0x40, 0x80, 0xcd, 0x06, 0x69, 0xc1, 0x94, 0x17, 0x7e, 0x63, 0xcb, 0xd6, 0x27, 0x3b, 0x8c, 0x4c, 0x29, 0x8a, 0xcb, 0xce, 0xa2, 0x21, 0x38, 0x94, 0x61, 0x61, 0x53, 0x63, 0x20, 0x87, 0x2e, 0x57, 0x4d, 0x0d, 0x81, 0x16, 0x39, 0x45, 0x00, 0x03, 0xcd, 0x34, 0x28, diff --git a/go/vt/proto/queryservice/queryservice.pb.go b/go/vt/proto/queryservice/queryservice.pb.go index 9c005868159..d7eb26e6354 100644 --- a/go/vt/proto/queryservice/queryservice.pb.go +++ b/go/vt/proto/queryservice/queryservice.pb.go @@ -934,7 +934,7 @@ func init() { proto.RegisterFile("queryservice.proto", fileDescriptor0) } var fileDescriptor0 = []byte{ // 491 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x09, 0x6e, 0x88, 0x02, 0xff, 0x7c, 0x95, 0xdb, 0x6e, 0xd4, 0x40, + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x7c, 0x95, 0xdb, 0x6e, 0xd4, 0x40, 0x0c, 0x86, 0xe1, 0xa2, 0x2d, 0x72, 0xc3, 0x69, 0x4a, 0x81, 0xa6, 0xa5, 0x2d, 0x7d, 0x80, 0x0a, 0x01, 0x12, 0x52, 0x25, 0x2e, 0xda, 0x08, 0x04, 0xaa, 0x38, 0x65, 0x59, 0x89, 0x2b, 0xa4, 0xd9, 0xc4, 0x5a, 0xa2, 0xcd, 0x26, 0xd9, 0xc9, 0x04, 0xc1, 0x13, 0xf1, 0x9a, 0x88, 0x4c, 0xec, 0xcc, diff --git a/go/vt/proto/replicationdata/replicationdata.pb.go b/go/vt/proto/replicationdata/replicationdata.pb.go index 667bd10090c..f88200ed348 100644 --- a/go/vt/proto/replicationdata/replicationdata.pb.go +++ b/go/vt/proto/replicationdata/replicationdata.pb.go @@ -102,7 +102,7 @@ func init() { proto.RegisterFile("replicationdata.proto", fileDescriptor0) } var fileDescriptor0 = []byte{ // 241 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x09, 0x6e, 0x88, 0x02, 0xff, 0x5c, 0xd0, 0xc1, 0x4a, 0x03, 0x31, + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x5c, 0xd0, 0xc1, 0x4a, 0x03, 0x31, 0x10, 0xc6, 0x71, 0x52, 0xed, 0x5a, 0x47, 0xb4, 0x1a, 0x2d, 0x04, 0x2f, 0x2e, 0x9e, 0x82, 0x07, 0x11, 0x7d, 0x03, 0xbd, 0xe8, 0x41, 0x90, 0xf4, 0x01, 0x42, 0xba, 0x1b, 0x6c, 0x60, 0xcd, 0x6c, 0x33, 0x53, 0xc1, 0xd7, 0xf1, 0x49, 0xa5, 0x49, 0xbb, 0x48, 0x8f, 0xf9, 0xfe, 0xbf, 0x43, 0x18, diff --git a/go/vt/proto/tableacl/tableacl.pb.go b/go/vt/proto/tableacl/tableacl.pb.go index 2baa4ecbf7c..2be03046be0 100644 --- a/go/vt/proto/tableacl/tableacl.pb.go +++ b/go/vt/proto/tableacl/tableacl.pb.go @@ -104,7 +104,7 @@ func init() { proto.RegisterFile("tableacl.proto", fileDescriptor0) } var fileDescriptor0 = []byte{ // 207 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x09, 0x6e, 0x88, 0x02, 0xff, 0xe2, 0xe2, 0x2b, 0x49, 0x4c, 0xca, + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0xe2, 0x2b, 0x49, 0x4c, 0xca, 0x49, 0x4d, 0x4c, 0xce, 0xd1, 0x2b, 0x28, 0xca, 0x2f, 0xc9, 0x17, 0xe2, 0x80, 0xf1, 0x95, 0x96, 0x33, 0x72, 0xf1, 0x85, 0x80, 0x38, 0xee, 0x45, 0xf9, 0xa5, 0x05, 0xc1, 0x05, 0xa9, 0xc9, 0x42, 0x42, 0x5c, 0x2c, 0x79, 0x89, 0xb9, 0xa9, 0x12, 0x8c, 0x0a, 0x8c, 0x1a, 0x9c, 0x41, 0x60, 0xb6, diff --git a/go/vt/proto/tabletmanagerdata/tabletmanagerdata.pb.go b/go/vt/proto/tabletmanagerdata/tabletmanagerdata.pb.go index 280956aa4cd..52749ea97eb 100644 --- a/go/vt/proto/tabletmanagerdata/tabletmanagerdata.pb.go +++ b/go/vt/proto/tabletmanagerdata/tabletmanagerdata.pb.go @@ -1767,7 +1767,7 @@ func init() { proto.RegisterFile("tabletmanagerdata.proto", fileDescriptor0) } var fileDescriptor0 = []byte{ // 2049 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x09, 0x6e, 0x88, 0x02, 0xff, 0xcc, 0x59, 0x5b, 0x6f, 0x1b, 0xc7, + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xcc, 0x59, 0x5b, 0x6f, 0x1b, 0xc7, 0xf5, 0x07, 0x45, 0x49, 0x96, 0x0e, 0x2f, 0x22, 0x97, 0xba, 0x50, 0x0a, 0xfe, 0xba, 0xac, 0x9d, 0x7f, 0x54, 0x17, 0x55, 0x6a, 0x25, 0x0d, 0x82, 0x04, 0x29, 0xaa, 0xab, 0xed, 0xc4, 0x89, 0x95, 0x95, 0x2f, 0x45, 0x5f, 0x16, 0x43, 0xee, 0x11, 0xb9, 0xd0, 0x72, 0x77, 0x3d, 0x33, 0x2b, 0x89, diff --git a/go/vt/proto/tabletmanagerservice/tabletmanagerservice.pb.go b/go/vt/proto/tabletmanagerservice/tabletmanagerservice.pb.go index b248d39f21d..e8ea7feaa58 100644 --- a/go/vt/proto/tabletmanagerservice/tabletmanagerservice.pb.go +++ b/go/vt/proto/tabletmanagerservice/tabletmanagerservice.pb.go @@ -1633,7 +1633,7 @@ func init() { proto.RegisterFile("tabletmanagerservice.proto", fileDescriptor0) var fileDescriptor0 = []byte{ // 969 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x09, 0x6e, 0x88, 0x02, 0xff, 0x94, 0x98, 0x6d, 0x6f, 0x1c, 0x35, + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x94, 0x98, 0x6d, 0x6f, 0x1c, 0x35, 0x10, 0xc7, 0x39, 0x09, 0x0a, 0x98, 0xc7, 0x5a, 0x88, 0xa2, 0x20, 0x01, 0x4d, 0x5a, 0x1e, 0x52, 0x54, 0xf5, 0x81, 0xf2, 0xfe, 0x2e, 0xbd, 0xb6, 0x41, 0x44, 0x1c, 0x77, 0x8d, 0x82, 0x84, 0x84, 0xe4, 0xec, 0x4d, 0x6f, 0x97, 0x78, 0x6d, 0x63, 0x7b, 0xa3, 0xe4, 0x15, 0x12, 0x12, 0xaf, 0x90, diff --git a/go/vt/proto/throttlerdata/throttlerdata.pb.go b/go/vt/proto/throttlerdata/throttlerdata.pb.go index 3e7c25463c1..812d0575851 100644 --- a/go/vt/proto/throttlerdata/throttlerdata.pb.go +++ b/go/vt/proto/throttlerdata/throttlerdata.pb.go @@ -436,7 +436,7 @@ func init() { proto.RegisterFile("throttlerdata.proto", fileDescriptor0) } var fileDescriptor0 = []byte{ // 711 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x09, 0x6e, 0x88, 0x02, 0xff, 0xa4, 0x55, 0xdd, 0x4e, 0xdb, 0x4a, + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xa4, 0x55, 0xdd, 0x4e, 0xdb, 0x4a, 0x10, 0x96, 0x09, 0xe1, 0xc0, 0x84, 0x00, 0x59, 0x38, 0x60, 0xc2, 0xd1, 0x51, 0x8e, 0xa5, 0xa3, 0x46, 0x48, 0xcd, 0x45, 0x50, 0x55, 0x5a, 0x54, 0x09, 0x52, 0xaa, 0xaa, 0x55, 0xcb, 0x85, 0x69, 0x7b, 0xd1, 0x9b, 0xd5, 0xc6, 0x1e, 0x1c, 0x0b, 0xdb, 0xeb, 0xee, 0x2e, 0x25, 0xe9, 0x43, 0xf4, diff --git a/go/vt/proto/throttlerservice/throttlerservice.pb.go b/go/vt/proto/throttlerservice/throttlerservice.pb.go index 66841caef2c..2dee35d9275 100644 --- a/go/vt/proto/throttlerservice/throttlerservice.pb.go +++ b/go/vt/proto/throttlerservice/throttlerservice.pb.go @@ -267,7 +267,7 @@ func init() { proto.RegisterFile("throttlerservice.proto", fileDescriptor0) } var fileDescriptor0 = []byte{ // 214 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x09, 0x6e, 0x88, 0x02, 0xff, 0xe2, 0x12, 0x2b, 0xc9, 0x28, 0xca, + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0x12, 0x2b, 0xc9, 0x28, 0xca, 0x2f, 0x29, 0xc9, 0x49, 0x2d, 0x2a, 0x4e, 0x2d, 0x2a, 0xcb, 0x4c, 0x4e, 0xd5, 0x2b, 0x28, 0xca, 0x2f, 0xc9, 0x17, 0x12, 0x40, 0x17, 0x97, 0x12, 0x86, 0x8b, 0xa4, 0x24, 0x96, 0x24, 0x42, 0x94, 0x19, 0x7d, 0x66, 0xe6, 0xe2, 0x0c, 0x81, 0x89, 0x0b, 0xf9, 0x72, 0x71, 0xf8, 0x26, 0x56, 0x04, diff --git a/go/vt/proto/topodata/topodata.pb.go b/go/vt/proto/topodata/topodata.pb.go index 38ba2c01d78..7d072310116 100644 --- a/go/vt/proto/topodata/topodata.pb.go +++ b/go/vt/proto/topodata/topodata.pb.go @@ -786,7 +786,7 @@ func init() { proto.RegisterFile("topodata.proto", fileDescriptor0) } var fileDescriptor0 = []byte{ // 1096 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x09, 0x6e, 0x88, 0x02, 0xff, 0xac, 0x56, 0x6f, 0x6f, 0xe3, 0xc4, + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xac, 0x56, 0x6f, 0x6f, 0xe3, 0xc4, 0x13, 0xfe, 0xd9, 0x71, 0xd2, 0x64, 0x9c, 0xe6, 0x7c, 0xfb, 0xbb, 0x43, 0x96, 0x11, 0xa2, 0x8a, 0x84, 0xa8, 0x0e, 0x11, 0x50, 0x8e, 0x83, 0xea, 0x24, 0xa4, 0xa6, 0xa9, 0x0f, 0xd2, 0x3f, 0x69, 0xd8, 0xa4, 0x82, 0xbe, 0xb2, 0x9c, 0x78, 0xdb, 0xb3, 0xea, 0x64, 0xcd, 0xee, 0xa6, 0x52, 0x3e, diff --git a/go/vt/proto/vschema/vschema.pb.go b/go/vt/proto/vschema/vschema.pb.go index 7e269a2bd35..2bdec892640 100644 --- a/go/vt/proto/vschema/vschema.pb.go +++ b/go/vt/proto/vschema/vschema.pb.go @@ -231,7 +231,7 @@ func init() { proto.RegisterFile("vschema.proto", fileDescriptor0) } var fileDescriptor0 = []byte{ // 436 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x09, 0x6e, 0x88, 0x02, 0xff, 0x74, 0x93, 0xd1, 0x6a, 0xd4, 0x40, + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x74, 0x93, 0xd1, 0x6a, 0xd4, 0x40, 0x14, 0x86, 0x99, 0xc4, 0x4d, 0xb3, 0x27, 0x26, 0xd5, 0xa1, 0x96, 0x10, 0x11, 0x97, 0xa0, 0xb8, 0x57, 0xb9, 0xd8, 0x22, 0x68, 0x45, 0x51, 0x8a, 0x17, 0x45, 0x41, 0x49, 0xa5, 0xb7, 0x65, 0x9a, 0x3d, 0xd0, 0xd2, 0xcd, 0x24, 0x66, 0x92, 0x68, 0x5e, 0xc5, 0x1b, 0xc1, 0x37, 0xf0, 0x0d, 0xa5, diff --git a/go/vt/proto/vtctldata/vtctldata.pb.go b/go/vt/proto/vtctldata/vtctldata.pb.go index 3a9025a0954..c0785666c56 100644 --- a/go/vt/proto/vtctldata/vtctldata.pb.go +++ b/go/vt/proto/vtctldata/vtctldata.pb.go @@ -82,7 +82,7 @@ func init() { proto.RegisterFile("vtctldata.proto", fileDescriptor0) } var fileDescriptor0 = []byte{ // 175 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x09, 0x6e, 0x88, 0x02, 0xff, 0xe2, 0xe2, 0x2f, 0x2b, 0x49, 0x2e, + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0xe2, 0x2f, 0x2b, 0x49, 0x2e, 0xc9, 0x49, 0x49, 0x2c, 0x49, 0xd4, 0x2b, 0x28, 0xca, 0x2f, 0xc9, 0x17, 0xe2, 0x84, 0x0b, 0x48, 0xf1, 0xe6, 0xe4, 0xa7, 0x97, 0x96, 0x64, 0xe6, 0x40, 0x64, 0x94, 0xc2, 0xb9, 0xa4, 0x5c, 0x2b, 0x52, 0x93, 0x4b, 0x4b, 0x52, 0xc3, 0x40, 0x4a, 0x9c, 0xf3, 0x73, 0x73, 0x13, 0xf3, 0x52, 0x82, diff --git a/go/vt/proto/vtctlservice/vtctlservice.pb.go b/go/vt/proto/vtctlservice/vtctlservice.pb.go index 094c4197ff9..16c84edc29f 100644 --- a/go/vt/proto/vtctlservice/vtctlservice.pb.go +++ b/go/vt/proto/vtctlservice/vtctlservice.pb.go @@ -136,7 +136,7 @@ func init() { proto.RegisterFile("vtctlservice.proto", fileDescriptor0) } var fileDescriptor0 = []byte{ // 118 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x09, 0x6e, 0x88, 0x02, 0xff, 0xe2, 0x12, 0x2a, 0x2b, 0x49, 0x2e, + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0x12, 0x2a, 0x2b, 0x49, 0x2e, 0xc9, 0x29, 0x4e, 0x2d, 0x2a, 0xcb, 0x4c, 0x4e, 0xd5, 0x2b, 0x28, 0xca, 0x2f, 0xc9, 0x17, 0xe2, 0x41, 0x16, 0x93, 0xe2, 0x07, 0xf3, 0x52, 0x12, 0x4b, 0x12, 0x21, 0xd2, 0x46, 0x85, 0x5c, 0xac, 0x61, 0x20, 0x21, 0xa1, 0x0c, 0x2e, 0x61, 0xd7, 0x8a, 0xd4, 0xe4, 0xd2, 0x92, 0x54, 0x30, 0xdf, diff --git a/go/vt/proto/vtgate/vtgate.pb.go b/go/vt/proto/vtgate/vtgate.pb.go index 179b0591340..b76b7c7c29f 100644 --- a/go/vt/proto/vtgate/vtgate.pb.go +++ b/go/vt/proto/vtgate/vtgate.pb.go @@ -82,6 +82,8 @@ type Session struct { // single_db specifies if the transaction should be restricted // to a single database. SingleDb bool `protobuf:"varint,3,opt,name=single_db,json=singleDb" json:"single_db,omitempty"` + // autocommit specifies if the session is in autocommit mode. + Autocommit bool `protobuf:"varint,4,opt,name=autocommit" json:"autocommit,omitempty"` } func (m *Session) Reset() { *m = Session{} } @@ -110,6 +112,13 @@ func (m *Session) GetSingleDb() bool { return false } +func (m *Session) GetAutocommit() bool { + if m != nil { + return m.Autocommit + } + return false +} + type Session_ShardSession struct { Target *query.Target `protobuf:"bytes,1,opt,name=target" json:"target,omitempty"` TransactionId int64 `protobuf:"varint,2,opt,name=transaction_id,json=transactionId" json:"transaction_id,omitempty"` @@ -148,8 +157,8 @@ type ExecuteRequest struct { TabletType topodata.TabletType `protobuf:"varint,4,opt,name=tablet_type,json=tabletType,enum=topodata.TabletType" json:"tablet_type,omitempty"` // not_in_transaction is deprecated and should not be used. NotInTransaction bool `protobuf:"varint,5,opt,name=not_in_transaction,json=notInTransaction" json:"not_in_transaction,omitempty"` - // keyspace to target the query to. - Keyspace string `protobuf:"bytes,6,opt,name=keyspace" json:"keyspace,omitempty"` + // keyspace_shard can be 'keyspace' or 'keyspace/shard'. + KeyspaceShard string `protobuf:"bytes,6,opt,name=keyspace_shard,json=keyspaceShard" json:"keyspace_shard,omitempty"` // options Options *query.ExecuteOptions `protobuf:"bytes,7,opt,name=options" json:"options,omitempty"` } @@ -194,9 +203,9 @@ func (m *ExecuteRequest) GetNotInTransaction() bool { return false } -func (m *ExecuteRequest) GetKeyspace() string { +func (m *ExecuteRequest) GetKeyspaceShard() string { if m != nil { - return m.Keyspace + return m.KeyspaceShard } return "" } @@ -791,13 +800,12 @@ type ExecuteBatchRequest struct { Queries []*query.BoundQuery `protobuf:"bytes,3,rep,name=queries" json:"queries,omitempty"` // tablet_type is the type of tablets that these queries is targeted to. TabletType topodata.TabletType `protobuf:"varint,4,opt,name=tablet_type,json=tabletType,enum=topodata.TabletType" json:"tablet_type,omitempty"` - // as_transaction will execute the queries in this batch in a single transaction per shard, - // created for this purpose. - // (this can be seen as adding a 'begin' before and 'commit' after the queries). - // Only makes sense if tablet_type is master. If set, the Session is ignored. + // as_transaction is deprecated. + // We cannot use the proto3 deprecated feature yet because + // the php compiler doesn't recognize that construct. AsTransaction bool `protobuf:"varint,5,opt,name=as_transaction,json=asTransaction" json:"as_transaction,omitempty"` - // keyspace to target the queries to. - Keyspace string `protobuf:"bytes,6,opt,name=keyspace" json:"keyspace,omitempty"` + // keyspace_shard can be 'keyspace' or 'keyspace/shard'. + KeyspaceShard string `protobuf:"bytes,6,opt,name=keyspace_shard,json=keyspaceShard" json:"keyspace_shard,omitempty"` // options Options *query.ExecuteOptions `protobuf:"bytes,7,opt,name=options" json:"options,omitempty"` } @@ -842,9 +850,9 @@ func (m *ExecuteBatchRequest) GetAsTransaction() bool { return false } -func (m *ExecuteBatchRequest) GetKeyspace() string { +func (m *ExecuteBatchRequest) GetKeyspaceShard() string { if m != nil { - return m.Keyspace + return m.KeyspaceShard } return "" } @@ -1191,8 +1199,8 @@ type StreamExecuteRequest struct { Query *query.BoundQuery `protobuf:"bytes,2,opt,name=query" json:"query,omitempty"` // tablet_type is the type of tablets that this query is targeted to. TabletType topodata.TabletType `protobuf:"varint,3,opt,name=tablet_type,json=tabletType,enum=topodata.TabletType" json:"tablet_type,omitempty"` - // keyspace to target the query to. - Keyspace string `protobuf:"bytes,4,opt,name=keyspace" json:"keyspace,omitempty"` + // keyspace_shard can be 'keyspace' or 'keyspace/shard'. + KeyspaceShard string `protobuf:"bytes,4,opt,name=keyspace_shard,json=keyspaceShard" json:"keyspace_shard,omitempty"` // options Options *query.ExecuteOptions `protobuf:"bytes,5,opt,name=options" json:"options,omitempty"` } @@ -1223,9 +1231,9 @@ func (m *StreamExecuteRequest) GetTabletType() topodata.TabletType { return topodata.TabletType_UNKNOWN } -func (m *StreamExecuteRequest) GetKeyspace() string { +func (m *StreamExecuteRequest) GetKeyspaceShard() string { if m != nil { - return m.Keyspace + return m.KeyspaceShard } return "" } @@ -2254,109 +2262,110 @@ func init() { func init() { proto.RegisterFile("vtgate.proto", fileDescriptor0) } var fileDescriptor0 = []byte{ - // 1652 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x09, 0x6e, 0x88, 0x02, 0xff, 0xd4, 0x5a, 0xcb, 0x6e, 0x1b, 0x47, - 0x16, 0x45, 0x77, 0xf3, 0x79, 0x49, 0x51, 0x52, 0x89, 0x92, 0x69, 0x5a, 0x63, 0xc9, 0x8d, 0x11, - 0x4c, 0x8f, 0x05, 0x7a, 0x2c, 0xcf, 0x0b, 0xb3, 0x99, 0xb1, 0x64, 0x61, 0x20, 0x78, 0xec, 0x38, - 0x25, 0xc5, 0x49, 0x80, 0x18, 0x8d, 0x16, 0x59, 0x90, 0x3a, 0x24, 0xbb, 0xe9, 0xae, 0x6a, 0x3a, - 0xca, 0x22, 0xf0, 0x1f, 0x18, 0x59, 0x04, 0x08, 0x8c, 0x00, 0x41, 0x80, 0x6c, 0xb3, 0x0d, 0x10, - 0x64, 0x93, 0x45, 0x90, 0x7c, 0x42, 0xf6, 0xf9, 0x81, 0x3c, 0xbe, 0x20, 0xe8, 0xaa, 0xea, 0x07, - 0x5b, 0x22, 0x45, 0x51, 0xa2, 0x41, 0xaf, 0x54, 0xcf, 0x5b, 0xa7, 0xce, 0x3d, 0x75, 0xeb, 0xb2, - 0x5a, 0x50, 0xec, 0xb1, 0x03, 0x93, 0x91, 0x7a, 0xd7, 0x75, 0x98, 0x83, 0x32, 0xa2, 0x56, 0x2d, - 0x3c, 0xf5, 0x88, 0x7b, 0x24, 0x1a, 0xab, 0x25, 0xe6, 0x74, 0x9d, 0xa6, 0xc9, 0x4c, 0x59, 0x2f, - 0xf4, 0x98, 0xdb, 0x6d, 0x88, 0x8a, 0xfe, 0xab, 0x02, 0xd9, 0x5d, 0x42, 0xa9, 0xe5, 0xd8, 0x68, - 0x0d, 0x4a, 0x96, 0x6d, 0x30, 0xd7, 0xb4, 0xa9, 0xd9, 0x60, 0x96, 0x63, 0x57, 0x94, 0x55, 0xa5, - 0x96, 0xc3, 0x33, 0x96, 0xbd, 0x17, 0x35, 0xa2, 0x2d, 0x28, 0xd1, 0x43, 0xd3, 0x6d, 0x1a, 0x54, - 0xcc, 0xa3, 0x15, 0x75, 0x55, 0xab, 0x15, 0x36, 0x96, 0xeb, 0x12, 0x8b, 0xb4, 0x57, 0xdf, 0xf5, - 0x47, 0xc9, 0x0a, 0x9e, 0xa1, 0xb1, 0x1a, 0x45, 0x57, 0x20, 0x4f, 0x2d, 0xfb, 0xa0, 0x4d, 0x8c, - 0xe6, 0x7e, 0x45, 0xe3, 0xcb, 0xe4, 0x44, 0xc3, 0xbd, 0xfd, 0xea, 0x7b, 0x50, 0x8c, 0xcf, 0x45, - 0x6b, 0x90, 0x61, 0xa6, 0x7b, 0x40, 0x18, 0x07, 0x54, 0xd8, 0x98, 0xa9, 0x8b, 0xfd, 0xed, 0xf1, - 0x46, 0x2c, 0x3b, 0x7d, 0xfc, 0x31, 0xf0, 0x86, 0xd5, 0xac, 0xa8, 0xab, 0x4a, 0x4d, 0xc3, 0x33, - 0xb1, 0xd6, 0x9d, 0xa6, 0xfe, 0xbd, 0x0a, 0xa5, 0xed, 0x0f, 0x48, 0xc3, 0x63, 0x04, 0x93, 0xa7, - 0x1e, 0xa1, 0x0c, 0xad, 0x43, 0xbe, 0x61, 0xb6, 0xdb, 0xc4, 0xf5, 0x27, 0x89, 0x35, 0x66, 0xeb, - 0x82, 0xa6, 0x2d, 0xde, 0xbe, 0x73, 0x0f, 0xe7, 0xc4, 0x88, 0x9d, 0x26, 0xba, 0x01, 0x59, 0xb9, - 0x75, 0xbe, 0x80, 0x18, 0x1b, 0xdf, 0x39, 0x0e, 0xfa, 0xd1, 0x75, 0x48, 0x73, 0xa8, 0x7c, 0x8b, - 0x85, 0x8d, 0x79, 0x09, 0x7c, 0xd3, 0xf1, 0xec, 0xe6, 0x9b, 0x7e, 0x11, 0x8b, 0x7e, 0xf4, 0x77, - 0x28, 0x30, 0x73, 0xbf, 0x4d, 0x98, 0xc1, 0x8e, 0xba, 0xa4, 0x92, 0x5a, 0x55, 0x6a, 0xa5, 0x8d, - 0x72, 0x3d, 0x74, 0xdd, 0x1e, 0xef, 0xdc, 0x3b, 0xea, 0x12, 0x0c, 0x2c, 0x2c, 0xa3, 0x75, 0x40, - 0xb6, 0xc3, 0x8c, 0x84, 0xdb, 0xd2, 0x9c, 0xcf, 0x39, 0xdb, 0x61, 0x3b, 0x7d, 0x9e, 0xab, 0x42, - 0xae, 0x45, 0x8e, 0x68, 0xd7, 0x6c, 0x90, 0x4a, 0x66, 0x55, 0xa9, 0xe5, 0x71, 0x58, 0x47, 0xb7, - 0x20, 0xeb, 0x74, 0x19, 0x77, 0x67, 0x96, 0x63, 0x5d, 0x94, 0x58, 0x25, 0x55, 0x6f, 0x88, 0x4e, - 0x1c, 0x8c, 0xd2, 0x5f, 0x28, 0x30, 0x1b, 0xd2, 0x48, 0xbb, 0x8e, 0x4d, 0x09, 0x5a, 0x83, 0x34, - 0x71, 0x5d, 0xc7, 0x4d, 0x70, 0x88, 0x1f, 0x6d, 0x6d, 0xfb, 0xcd, 0x58, 0xf4, 0x9e, 0x85, 0xc0, - 0xbf, 0x40, 0xc6, 0x25, 0xd4, 0x6b, 0x33, 0xc9, 0x20, 0x92, 0xa8, 0x04, 0x79, 0xbc, 0x07, 0xcb, - 0x11, 0xfa, 0xcf, 0x2a, 0x94, 0x25, 0x22, 0x2e, 0x1f, 0x3a, 0x3d, 0xee, 0x8d, 0x33, 0x9f, 0x4a, - 0x30, 0xbf, 0x04, 0x19, 0x7e, 0x36, 0x68, 0x25, 0xbd, 0xaa, 0xd5, 0xf2, 0x58, 0xd6, 0x92, 0x92, - 0xc8, 0x9c, 0x4b, 0x12, 0xd9, 0x01, 0x92, 0x88, 0xb9, 0x3d, 0x37, 0x92, 0xdb, 0x3f, 0x51, 0x60, - 0x31, 0x41, 0xf2, 0x54, 0x38, 0xff, 0x77, 0x15, 0x2e, 0x4b, 0x5c, 0xf7, 0x25, 0xb3, 0x3b, 0xaf, - 0x8b, 0x02, 0xae, 0x41, 0x31, 0x28, 0x1b, 0x96, 0xd4, 0x41, 0x11, 0x17, 0x5a, 0xd1, 0x3e, 0xa6, - 0x54, 0x0c, 0x2f, 0x15, 0xa8, 0x9e, 0x44, 0xfa, 0x54, 0x28, 0xe2, 0xb9, 0x06, 0x97, 0x22, 0x70, - 0xd8, 0xb4, 0x0f, 0xc8, 0x6b, 0xa2, 0x87, 0xdb, 0x00, 0x2d, 0x72, 0x64, 0xb8, 0x1c, 0x32, 0x57, - 0x83, 0xbf, 0xd3, 0xd0, 0xd7, 0xc1, 0x6e, 0x70, 0xbe, 0x15, 0xec, 0x6b, 0x4a, 0xf5, 0xf1, 0xa9, - 0x02, 0x95, 0xe3, 0x2e, 0x98, 0x0a, 0x75, 0x7c, 0x93, 0x0a, 0xd5, 0xb1, 0x6d, 0x33, 0x8b, 0x1d, - 0xbd, 0x36, 0xd1, 0x62, 0x1d, 0x10, 0xe1, 0x88, 0x8d, 0x86, 0xd3, 0xf6, 0x3a, 0xb6, 0x61, 0x9b, - 0x1d, 0xc2, 0xef, 0xfc, 0x3c, 0x9e, 0x13, 0x3d, 0x5b, 0xbc, 0xe3, 0xa1, 0xd9, 0x21, 0xe8, 0x1d, - 0x58, 0x90, 0xa3, 0xfb, 0x42, 0x4c, 0x86, 0x8b, 0xaa, 0x16, 0x20, 0x1d, 0xc0, 0x44, 0x3d, 0x68, - 0xc0, 0xf3, 0xc2, 0xc8, 0xfd, 0xc1, 0x21, 0x29, 0x7b, 0x2e, 0xc9, 0xe5, 0x4e, 0x97, 0x5c, 0x7e, - 0x14, 0xc9, 0x55, 0xf7, 0x21, 0x17, 0x80, 0x46, 0x2b, 0x90, 0xe2, 0xd0, 0x14, 0x0e, 0xad, 0x10, - 0x64, 0x8d, 0x3e, 0x22, 0xde, 0x81, 0xca, 0x90, 0xee, 0x99, 0x6d, 0x8f, 0x70, 0xc7, 0x15, 0xb1, - 0xa8, 0xa0, 0x15, 0x28, 0xc4, 0xb8, 0xe2, 0xbe, 0x2a, 0x62, 0x88, 0xa2, 0x71, 0x5c, 0xd6, 0x31, - 0xc6, 0xa6, 0x42, 0xd6, 0x3f, 0xa8, 0xb0, 0x20, 0xa1, 0x6d, 0x9a, 0xac, 0x71, 0x38, 0x71, 0x49, - 0xdf, 0x84, 0xac, 0x8f, 0xc6, 0x22, 0xb4, 0xa2, 0x71, 0x4d, 0x9d, 0x20, 0xea, 0x60, 0xc4, 0xb8, - 0x59, 0xee, 0x1a, 0x94, 0x4c, 0x7a, 0x42, 0x86, 0x3b, 0x63, 0xd2, 0x89, 0xa5, 0xb7, 0x2f, 0x95, - 0x30, 0x99, 0x94, 0x44, 0x4e, 0xcc, 0xbf, 0x7f, 0x85, 0xac, 0xf0, 0x5e, 0x40, 0xe1, 0x92, 0xc4, - 0x26, 0x7c, 0xfb, 0xb6, 0xc5, 0x0e, 0x85, 0xe9, 0x60, 0x98, 0x6e, 0xc3, 0x2c, 0xa7, 0x97, 0x67, - 0x60, 0x9c, 0xe3, 0x28, 0xb4, 0x28, 0x67, 0x08, 0x2d, 0xea, 0xc0, 0x54, 0x54, 0x8b, 0xa7, 0xa2, - 0xfa, 0xd7, 0x51, 0x72, 0xc5, 0xc9, 0x78, 0x45, 0xe9, 0xf5, 0xed, 0xa4, 0xb6, 0x2e, 0x05, 0x43, - 0x13, 0xbb, 0x7f, 0x55, 0x0a, 0x8b, 0xa9, 0x28, 0x33, 0x92, 0x8a, 0x3e, 0x8b, 0x12, 0xa4, 0x3e, - 0xe2, 0x26, 0xa6, 0xa5, 0xf5, 0xa4, 0x96, 0x4e, 0x0a, 0x16, 0xa1, 0x8e, 0x3e, 0x82, 0x32, 0x67, - 0x32, 0x0a, 0xeb, 0x17, 0x28, 0xa6, 0x64, 0x56, 0xab, 0x1d, 0xcb, 0x6a, 0xf5, 0xef, 0x54, 0xb8, - 0x1a, 0xa7, 0xe7, 0x55, 0x66, 0xee, 0xff, 0x48, 0x8a, 0x6b, 0xb9, 0x4f, 0x5c, 0x09, 0x4a, 0xa6, - 0x56, 0x61, 0x5f, 0x28, 0xb0, 0x32, 0x90, 0xc2, 0x29, 0x91, 0xd9, 0x6f, 0x0a, 0x94, 0x77, 0x99, - 0x4b, 0xcc, 0xce, 0xb9, 0xde, 0x5d, 0x42, 0x55, 0xaa, 0x67, 0x7b, 0x4c, 0xd1, 0x46, 0x74, 0xd1, - 0xb0, 0xa4, 0x2b, 0xe6, 0x97, 0xf4, 0x48, 0x7e, 0xd9, 0x82, 0xc5, 0xc4, 0x96, 0xa5, 0x33, 0xa2, - 0xdb, 0x5c, 0x39, 0xf5, 0x36, 0x7f, 0xa1, 0x42, 0xb5, 0xcf, 0xca, 0x79, 0x02, 0xef, 0xc8, 0xf4, - 0xc5, 0x79, 0xd0, 0x06, 0xde, 0x10, 0xa9, 0x61, 0x8f, 0x15, 0xe9, 0x11, 0x29, 0x3f, 0xb3, 0xdc, - 0x77, 0xe0, 0xca, 0x89, 0x84, 0x8c, 0x41, 0xee, 0xe7, 0x2a, 0xac, 0xf4, 0xd9, 0x3a, 0x77, 0xf4, - 0xb9, 0x10, 0x86, 0x93, 0x61, 0x33, 0x75, 0xea, 0x63, 0xc0, 0xc4, 0xc8, 0x7e, 0x08, 0xab, 0x83, - 0x09, 0x1a, 0x83, 0xf1, 0xaf, 0x54, 0xf8, 0x53, 0xd2, 0xe0, 0x79, 0x7e, 0x97, 0x5f, 0x08, 0xdf, - 0xfd, 0x3f, 0xb6, 0x53, 0x63, 0xfc, 0xd8, 0x9e, 0x18, 0xff, 0xff, 0x87, 0xab, 0x83, 0xe8, 0x1a, - 0x83, 0xfd, 0x77, 0xa1, 0xb8, 0x49, 0x0e, 0x2c, 0x7b, 0x3c, 0xae, 0xfb, 0x1e, 0xec, 0xd5, 0xfe, - 0x07, 0x7b, 0xfd, 0xdf, 0x30, 0x23, 0x4d, 0x4b, 0x5c, 0xb1, 0xab, 0x44, 0x19, 0x7e, 0x95, 0xe8, - 0xcf, 0x15, 0x98, 0xd9, 0x72, 0x3a, 0x1d, 0x8b, 0x4d, 0xfc, 0xca, 0x5f, 0x82, 0x8c, 0xc9, 0x9c, - 0x8e, 0xd5, 0x90, 0x5f, 0x1c, 0x64, 0x4d, 0x9f, 0x83, 0x52, 0x80, 0x40, 0xe0, 0xd7, 0xdf, 0x87, - 0x59, 0xec, 0xb4, 0xdb, 0xfb, 0x66, 0xa3, 0x35, 0x69, 0x54, 0x3a, 0x82, 0xb9, 0x68, 0x2d, 0xb9, - 0xfe, 0x13, 0xb8, 0x8c, 0x09, 0x75, 0xda, 0x3d, 0x12, 0x4b, 0x0e, 0xc6, 0x43, 0x82, 0x20, 0xd5, - 0x64, 0xf2, 0x5b, 0x48, 0x1e, 0xf3, 0xb2, 0xfe, 0xad, 0x02, 0xe5, 0x07, 0x84, 0x52, 0xf3, 0x80, - 0x08, 0x81, 0x8d, 0x67, 0x7a, 0x58, 0xf6, 0x57, 0x86, 0x34, 0xbf, 0x1a, 0xe4, 0x79, 0x13, 0x15, - 0x74, 0x0b, 0xf2, 0xe1, 0x61, 0xe3, 0x77, 0xec, 0xc9, 0x67, 0x2d, 0x17, 0x9c, 0x35, 0x1f, 0x7d, - 0xec, 0x79, 0x83, 0x97, 0xf5, 0x8f, 0x15, 0x98, 0x97, 0xe8, 0xef, 0x8e, 0xeb, 0x9f, 0x61, 0xd0, - 0x83, 0x35, 0xb5, 0x68, 0x4d, 0x74, 0x15, 0xb4, 0x20, 0x18, 0x17, 0x36, 0x8a, 0xf2, 0x94, 0x3d, - 0x36, 0xdb, 0x1e, 0xc1, 0x7e, 0x87, 0xbe, 0x0c, 0xd5, 0x93, 0x1c, 0x26, 0xdd, 0xf9, 0x8b, 0x0a, - 0xf3, 0xbb, 0xdd, 0xb6, 0xc5, 0xe4, 0xb9, 0xbc, 0x68, 0xc4, 0x23, 0xbf, 0x2b, 0x5d, 0x83, 0x22, - 0xf5, 0x71, 0xc8, 0xa7, 0x23, 0x79, 0x89, 0x17, 0x78, 0x9b, 0x78, 0x34, 0x42, 0x2b, 0x50, 0x08, - 0x86, 0x78, 0x36, 0xe3, 0xc4, 0x6b, 0x18, 0xe4, 0x08, 0xcf, 0x66, 0xe8, 0x6f, 0x70, 0xc9, 0xf6, - 0x3a, 0x86, 0xeb, 0x3c, 0xa3, 0x46, 0x97, 0xb8, 0x06, 0xb7, 0x6c, 0x74, 0x4d, 0x97, 0xf1, 0xb0, - 0xa6, 0xe1, 0x05, 0xdb, 0xeb, 0x60, 0xe7, 0x19, 0x7d, 0x44, 0x5c, 0xbe, 0xf8, 0x23, 0xd3, 0x65, - 0xe8, 0xbf, 0x90, 0x37, 0xdb, 0x07, 0x8e, 0x6b, 0xb1, 0xc3, 0x8e, 0x7c, 0x2b, 0xd2, 0x25, 0xcc, - 0x63, 0xcc, 0xd4, 0xef, 0x06, 0x23, 0x71, 0x34, 0x09, 0xdd, 0x04, 0xe4, 0x51, 0x62, 0x08, 0x70, - 0x62, 0xd1, 0xde, 0x86, 0x7c, 0x38, 0x9a, 0xf5, 0x28, 0x89, 0xcc, 0x3c, 0xde, 0xd0, 0x7f, 0xd4, - 0x00, 0xc5, 0xed, 0xca, 0xb8, 0xf4, 0x4f, 0xc8, 0xf0, 0xf9, 0xb4, 0xa2, 0x70, 0x4f, 0xae, 0x84, - 0xa7, 0xf2, 0xd8, 0xd8, 0xba, 0x0f, 0x1b, 0xcb, 0xe1, 0xd5, 0x27, 0x50, 0x0c, 0xd4, 0xc9, 0xb7, - 0x13, 0xf7, 0x86, 0x32, 0xf4, 0x46, 0x51, 0x47, 0xb8, 0x51, 0xaa, 0xff, 0x81, 0x3c, 0xcf, 0x64, - 0x4e, 0xb5, 0x1d, 0xe5, 0x5f, 0x6a, 0x3c, 0xff, 0xaa, 0xfe, 0xa4, 0x40, 0x8a, 0x4f, 0x1e, 0xf9, - 0xa7, 0xdb, 0x03, 0x28, 0x85, 0x28, 0x85, 0xf7, 0x44, 0xa0, 0xba, 0x3e, 0x84, 0x92, 0x38, 0x05, - 0xb8, 0xd8, 0x8a, 0x13, 0xb2, 0x05, 0x20, 0xbe, 0x0a, 0x73, 0x53, 0x42, 0x87, 0x7f, 0x1e, 0x62, - 0x2a, 0xdc, 0x2e, 0xce, 0xd3, 0x70, 0xe7, 0x08, 0x52, 0xd4, 0xfa, 0x50, 0x44, 0x06, 0x0d, 0xf3, - 0xb2, 0x7e, 0x07, 0x16, 0xff, 0x47, 0xd8, 0xae, 0xdb, 0x0b, 0xb2, 0x8f, 0xe0, 0xf8, 0x0c, 0xa1, - 0x49, 0xc7, 0xb0, 0x94, 0x9c, 0x24, 0x15, 0xf0, 0x2f, 0x28, 0x52, 0xb7, 0x67, 0xf4, 0xcd, 0xf4, - 0x6f, 0xe2, 0xd0, 0x3d, 0xf1, 0x49, 0x05, 0x1a, 0x55, 0xf4, 0x2f, 0x55, 0x58, 0x78, 0xab, 0xdb, - 0x34, 0xd9, 0xb4, 0xc7, 0xcc, 0x31, 0xd3, 0x93, 0x65, 0xc8, 0x33, 0xab, 0x43, 0x28, 0x33, 0x3b, - 0x5d, 0x79, 0x92, 0xa3, 0x06, 0x5f, 0x57, 0xa4, 0x47, 0x6c, 0x26, 0x9f, 0xcf, 0x02, 0x5d, 0x6d, - 0xfb, 0x6d, 0x7b, 0x4e, 0x8b, 0xd8, 0x58, 0xf4, 0xeb, 0x2d, 0x28, 0xf7, 0xb3, 0x24, 0x89, 0xaf, - 0x05, 0x06, 0xfa, 0x33, 0x15, 0x99, 0xe0, 0xf8, 0x3d, 0xd2, 0x02, 0xba, 0x01, 0x73, 0x7e, 0xca, - 0xd2, 0x21, 0x46, 0x84, 0x47, 0x7c, 0xc9, 0x9f, 0x15, 0xed, 0x7b, 0x41, 0xf3, 0x66, 0x15, 0x2a, - 0x0d, 0xa7, 0x53, 0x3f, 0x72, 0x3c, 0xe6, 0xed, 0x93, 0x7a, 0xcf, 0x62, 0x84, 0x52, 0xf1, 0xaf, - 0x0d, 0xfb, 0x19, 0xfe, 0xe7, 0xce, 0x1f, 0x01, 0x00, 0x00, 0xff, 0xff, 0xcc, 0x69, 0x78, 0xfc, - 0x23, 0x21, 0x00, 0x00, + // 1678 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xd4, 0x5a, 0x4b, 0x6f, 0xdb, 0xc6, + 0x16, 0x06, 0x49, 0x3d, 0x8f, 0x1e, 0xb6, 0xc7, 0xb2, 0xa3, 0x28, 0xbe, 0xb6, 0x43, 0x5c, 0x23, + 0xce, 0x8d, 0xa1, 0xdc, 0x38, 0xf7, 0x85, 0xbb, 0xb9, 0x37, 0x76, 0x8c, 0x0b, 0x23, 0x37, 0x69, + 0x3a, 0x76, 0xd3, 0x16, 0x68, 0x40, 0xd0, 0xd2, 0xc0, 0x66, 0x25, 0x91, 0x0a, 0x67, 0xa8, 0xd4, + 0x5d, 0x14, 0xf9, 0x07, 0x69, 0x17, 0x05, 0x8a, 0xa0, 0x40, 0x51, 0xa0, 0xdb, 0x6e, 0x0b, 0x14, + 0xdd, 0x74, 0xd7, 0x65, 0xd1, 0x55, 0xf7, 0xfd, 0x03, 0x05, 0xba, 0xee, 0xa2, 0xe0, 0xcc, 0xf0, + 0x21, 0xda, 0x92, 0x65, 0xf9, 0x01, 0x65, 0x65, 0xce, 0x83, 0x67, 0xbe, 0xf9, 0xce, 0x37, 0xe7, + 0x1c, 0x0d, 0x0d, 0xc5, 0x1e, 0xdb, 0x37, 0x19, 0xa9, 0x77, 0x5d, 0x87, 0x39, 0x28, 0x23, 0x5a, + 0xb5, 0xc2, 0x33, 0x8f, 0xb8, 0x87, 0xa2, 0xb3, 0x56, 0x66, 0x4e, 0xd7, 0x69, 0x9a, 0xcc, 0x94, + 0xed, 0x42, 0x8f, 0xb9, 0xdd, 0x86, 0x68, 0xe8, 0x1f, 0xab, 0x90, 0xdd, 0x21, 0x94, 0x5a, 0x8e, + 0x8d, 0x56, 0xa0, 0x6c, 0xd9, 0x06, 0x73, 0x4d, 0x9b, 0x9a, 0x0d, 0x66, 0x39, 0x76, 0x55, 0x59, + 0x56, 0x56, 0x73, 0xb8, 0x64, 0xd9, 0xbb, 0x51, 0x27, 0xda, 0x84, 0x32, 0x3d, 0x30, 0xdd, 0xa6, + 0x41, 0xc5, 0x7b, 0xb4, 0xaa, 0x2e, 0x6b, 0xab, 0x85, 0xf5, 0x85, 0xba, 0xc4, 0x22, 0xed, 0xd5, + 0x77, 0xfc, 0x59, 0xb2, 0x81, 0x4b, 0x34, 0xd6, 0xa2, 0xe8, 0x1a, 0xe4, 0xa9, 0x65, 0xef, 0xb7, + 0x89, 0xd1, 0xdc, 0xab, 0x6a, 0x7c, 0x99, 0x9c, 0xe8, 0xb8, 0xbf, 0x87, 0x16, 0x01, 0x4c, 0x8f, + 0x39, 0x0d, 0xa7, 0xd3, 0xb1, 0x58, 0x35, 0xc5, 0x47, 0x63, 0x3d, 0xb5, 0xf7, 0xa0, 0x18, 0xb7, + 0x8d, 0x56, 0x20, 0xc3, 0x4c, 0x77, 0x9f, 0x30, 0x0e, 0xb8, 0xb0, 0x5e, 0xaa, 0x8b, 0xfd, 0xef, + 0xf2, 0x4e, 0x2c, 0x07, 0xfd, 0xfd, 0xc5, 0x36, 0x67, 0x58, 0xcd, 0xaa, 0xba, 0xac, 0xac, 0x6a, + 0xb8, 0x14, 0xeb, 0xdd, 0x6e, 0xea, 0x3f, 0xaa, 0x50, 0xde, 0xfa, 0x80, 0x34, 0x3c, 0x46, 0x30, + 0x79, 0xe6, 0x11, 0xca, 0xd0, 0x1a, 0xe4, 0x1b, 0x66, 0xbb, 0x4d, 0x5c, 0xff, 0x25, 0xb1, 0xc6, + 0x54, 0x5d, 0xd0, 0xb8, 0xc9, 0xfb, 0xb7, 0xef, 0xe3, 0x9c, 0x98, 0xb1, 0xdd, 0x44, 0x37, 0x21, + 0x2b, 0xa9, 0xe1, 0x0b, 0x88, 0xb9, 0x71, 0x66, 0x70, 0x30, 0x8e, 0x6e, 0x40, 0x9a, 0x43, 0xe5, + 0x14, 0x14, 0xd6, 0x67, 0x24, 0xf0, 0x0d, 0xc7, 0xb3, 0x9b, 0x6f, 0xfa, 0x8f, 0x58, 0x8c, 0xa3, + 0xbf, 0x43, 0x81, 0x99, 0x7b, 0x6d, 0xc2, 0x0c, 0x76, 0xd8, 0x25, 0x9c, 0x93, 0xf2, 0x7a, 0xa5, + 0x1e, 0xba, 0x76, 0x97, 0x0f, 0xee, 0x1e, 0x76, 0x09, 0x06, 0x16, 0x3e, 0xa3, 0x35, 0x40, 0xb6, + 0xc3, 0x8c, 0x84, 0x5b, 0xd3, 0x9c, 0xd1, 0x69, 0xdb, 0x61, 0xdb, 0x7d, 0x9e, 0x5d, 0x81, 0x72, + 0x8b, 0x1c, 0xd2, 0xae, 0xd9, 0x20, 0x06, 0x77, 0x57, 0x35, 0xb3, 0xac, 0xac, 0xe6, 0x71, 0x29, + 0xe8, 0xe5, 0xac, 0xa3, 0xdb, 0x90, 0x75, 0xba, 0x8c, 0x7b, 0x3e, 0xcb, 0x61, 0xcf, 0x49, 0xd8, + 0x92, 0xb5, 0x37, 0xc4, 0x20, 0x0e, 0x66, 0xe9, 0x2f, 0x15, 0x98, 0x0a, 0x19, 0xa5, 0x5d, 0xc7, + 0xa6, 0x04, 0xad, 0x40, 0x9a, 0xb8, 0xae, 0xe3, 0x26, 0xe8, 0xc4, 0x8f, 0x37, 0xb7, 0xfc, 0x6e, + 0x2c, 0x46, 0x4f, 0xc3, 0xe5, 0x5f, 0x20, 0xe3, 0x12, 0xea, 0xb5, 0x99, 0x24, 0x13, 0x49, 0x54, + 0x82, 0x47, 0x3e, 0x82, 0xe5, 0x0c, 0xfd, 0x17, 0x15, 0x2a, 0x12, 0x11, 0xdf, 0x13, 0x9d, 0x1c, + 0x4f, 0xd7, 0x20, 0x17, 0xd0, 0xcd, 0xdd, 0x9c, 0xc7, 0x61, 0x1b, 0xcd, 0x43, 0x86, 0xfb, 0x85, + 0x56, 0xd3, 0xcb, 0xda, 0x6a, 0x1e, 0xcb, 0x56, 0x52, 0x1d, 0x99, 0x33, 0xa9, 0x23, 0x3b, 0x40, + 0x1d, 0x31, 0xb7, 0xe7, 0x46, 0x72, 0xfb, 0xa7, 0x0a, 0xcc, 0x25, 0x48, 0x9e, 0x08, 0xe7, 0xff, + 0xa6, 0xc2, 0x55, 0x89, 0xeb, 0x81, 0x64, 0x76, 0xfb, 0x75, 0x51, 0xc0, 0x75, 0x28, 0x86, 0x47, + 0xd4, 0x92, 0x3a, 0x28, 0xe2, 0x42, 0x2b, 0xda, 0xc7, 0x84, 0x8a, 0xe1, 0x95, 0x02, 0xb5, 0xe3, + 0x48, 0x9f, 0x08, 0x45, 0xbc, 0xd0, 0xe0, 0x4a, 0x04, 0x0e, 0x9b, 0xf6, 0x3e, 0x79, 0x4d, 0xf4, + 0x70, 0x07, 0xa0, 0x45, 0x0e, 0x0d, 0x97, 0x43, 0xe6, 0x6a, 0xf0, 0x77, 0x1a, 0xfa, 0x3a, 0xd8, + 0x0d, 0xce, 0xb7, 0x82, 0x7d, 0x4d, 0xa8, 0x3e, 0x3e, 0x53, 0xa0, 0x7a, 0xd4, 0x05, 0x13, 0xa1, + 0x8e, 0x6f, 0x53, 0xa1, 0x3a, 0xb6, 0x6c, 0x66, 0xb1, 0xc3, 0xd7, 0x26, 0x5a, 0xac, 0x01, 0x22, + 0x1c, 0xb1, 0xd1, 0x70, 0xda, 0x5e, 0xc7, 0x36, 0x6c, 0xb3, 0x43, 0x78, 0xfa, 0xcf, 0xe3, 0x69, + 0x31, 0xb2, 0xc9, 0x07, 0x1e, 0x99, 0x1d, 0x82, 0xde, 0x81, 0x59, 0x39, 0xbb, 0x2f, 0xc4, 0x64, + 0xb8, 0xa8, 0x56, 0x03, 0xa4, 0x03, 0x98, 0xa8, 0x07, 0x1d, 0x78, 0x46, 0x18, 0x79, 0x30, 0x38, + 0x24, 0x65, 0xcf, 0x24, 0xb9, 0xdc, 0xc9, 0x92, 0xcb, 0x8f, 0x22, 0xb9, 0xda, 0x1e, 0xe4, 0x02, + 0xd0, 0x68, 0x09, 0x52, 0x1c, 0x9a, 0xc2, 0xa1, 0x15, 0x82, 0x02, 0xd2, 0x47, 0xc4, 0x07, 0x50, + 0x05, 0xd2, 0x3d, 0xb3, 0xed, 0x11, 0xee, 0xb8, 0x22, 0x16, 0x0d, 0xb4, 0x04, 0x85, 0x18, 0x57, + 0xdc, 0x57, 0x45, 0x0c, 0x51, 0x34, 0x8e, 0xcb, 0x3a, 0xc6, 0xd8, 0x44, 0xc8, 0xfa, 0x27, 0x15, + 0x66, 0x25, 0xb4, 0x0d, 0x93, 0x35, 0x0e, 0x2e, 0x5c, 0xd2, 0xb7, 0x20, 0xeb, 0xa3, 0xb1, 0x08, + 0xad, 0x6a, 0x5c, 0x53, 0xc7, 0x88, 0x3a, 0x98, 0x31, 0x6e, 0xc1, 0xbb, 0x02, 0x65, 0x93, 0x1e, + 0x53, 0xec, 0x96, 0x4c, 0x7a, 0x19, 0x95, 0xee, 0x2b, 0x25, 0xac, 0x2b, 0x25, 0xa7, 0x17, 0xe6, + 0xea, 0xbf, 0x42, 0x56, 0x38, 0x32, 0x60, 0x73, 0x5e, 0x62, 0x13, 0x6e, 0x7e, 0xdb, 0x62, 0x07, + 0xc2, 0x74, 0x30, 0x4d, 0xb7, 0x61, 0x8a, 0x33, 0xcd, 0xf7, 0xc6, 0xe9, 0x8e, 0xa2, 0x8c, 0x72, + 0x8a, 0x28, 0xa3, 0x0e, 0xac, 0x4a, 0xb5, 0x78, 0x55, 0xaa, 0x7f, 0x13, 0xd5, 0x59, 0x9c, 0x8c, + 0x4b, 0xaa, 0xb4, 0xef, 0x24, 0x65, 0x76, 0x25, 0x98, 0x9a, 0xd8, 0xfd, 0x65, 0x89, 0x2d, 0xa6, + 0xa2, 0xcc, 0x48, 0x2a, 0xfa, 0x3c, 0xaa, 0x95, 0xfa, 0x88, 0xbb, 0x30, 0x2d, 0xad, 0x25, 0xb5, + 0x74, 0x5c, 0xdc, 0x08, 0x75, 0xf4, 0x11, 0x54, 0x38, 0x93, 0x51, 0x84, 0x3f, 0x47, 0x31, 0x25, + 0x0b, 0x5c, 0xed, 0x48, 0x81, 0xab, 0x7f, 0xaf, 0xc2, 0x62, 0x9c, 0x9e, 0xcb, 0x2c, 0xe2, 0xff, + 0x91, 0x14, 0xd7, 0x42, 0x9f, 0xb8, 0x12, 0x94, 0x4c, 0xac, 0xc2, 0xbe, 0x54, 0x60, 0x69, 0x20, + 0x85, 0x13, 0x22, 0xb3, 0xdf, 0x15, 0xa8, 0xec, 0x30, 0x97, 0x98, 0x9d, 0x33, 0xdd, 0xc6, 0x84, + 0xaa, 0x54, 0x4f, 0x77, 0xc5, 0xa2, 0x8d, 0xee, 0xa2, 0x44, 0x2a, 0x49, 0x9d, 0x90, 0x4a, 0xd2, + 0x23, 0xb9, 0x68, 0x13, 0xe6, 0x12, 0xbb, 0x97, 0x7e, 0x89, 0x72, 0xbc, 0x72, 0x62, 0x8e, 0x7f, + 0xa9, 0x42, 0xad, 0xcf, 0xca, 0x59, 0x62, 0xf0, 0xc8, 0x4c, 0xc6, 0xcf, 0xb7, 0x36, 0x30, 0x59, + 0xa4, 0x86, 0x5d, 0x61, 0xa4, 0x47, 0x64, 0xff, 0xd4, 0xca, 0xdf, 0x86, 0x6b, 0xc7, 0x12, 0x32, + 0x06, 0xb9, 0x5f, 0xa8, 0xb0, 0xd4, 0x67, 0xeb, 0xcc, 0x81, 0xe8, 0x5c, 0x18, 0x4e, 0x46, 0xd0, + 0xd4, 0x89, 0x57, 0x04, 0x17, 0x46, 0xf6, 0x23, 0x58, 0x1e, 0x4c, 0xd0, 0x18, 0x8c, 0x7f, 0xad, + 0xc2, 0x9f, 0x92, 0x06, 0xcf, 0xf2, 0x6b, 0xfd, 0x5c, 0xf8, 0xee, 0xff, 0x09, 0x9e, 0x1a, 0xe3, + 0x27, 0xf8, 0x85, 0xf1, 0xff, 0x7f, 0x58, 0x1c, 0x44, 0xd7, 0x18, 0xec, 0xbf, 0x0b, 0xc5, 0x0d, + 0xb2, 0x6f, 0xd9, 0xe3, 0x71, 0xdd, 0x77, 0xe3, 0xaf, 0xf6, 0xdf, 0xf8, 0xeb, 0xff, 0x86, 0x92, + 0x34, 0x2d, 0x71, 0xc5, 0xb2, 0x8a, 0x32, 0x3c, 0xab, 0xe8, 0x2f, 0x14, 0x28, 0x6d, 0xf2, 0x0f, + 0x03, 0x17, 0x9e, 0xfd, 0xe7, 0x21, 0x63, 0x32, 0xa7, 0x63, 0x35, 0xe4, 0x27, 0x0b, 0xd9, 0xd2, + 0xa7, 0xa1, 0x1c, 0x20, 0x10, 0xf8, 0xf5, 0xf7, 0x61, 0x0a, 0x3b, 0xed, 0xf6, 0x9e, 0xd9, 0x68, + 0x5d, 0x34, 0x2a, 0x1d, 0xc1, 0x74, 0xb4, 0x96, 0x5c, 0xff, 0x29, 0x5c, 0xc5, 0x84, 0x3a, 0xed, + 0x1e, 0x89, 0xd5, 0x09, 0xe3, 0x21, 0x41, 0x90, 0x6a, 0x32, 0xf9, 0xb1, 0x24, 0x8f, 0xf9, 0xb3, + 0xfe, 0x9d, 0x02, 0x95, 0x87, 0x84, 0x52, 0x73, 0x9f, 0x08, 0x81, 0x8d, 0x67, 0x7a, 0x58, 0x21, + 0x58, 0x81, 0xb4, 0x48, 0xa7, 0xe2, 0xbc, 0x89, 0x06, 0xba, 0x0d, 0xf9, 0xf0, 0xb0, 0xf1, 0x44, + 0x7b, 0xfc, 0x59, 0xcb, 0x05, 0x67, 0xcd, 0x47, 0x1f, 0xbb, 0xf4, 0xe0, 0xcf, 0xfa, 0x27, 0x0a, + 0xcc, 0x48, 0xf4, 0xf7, 0xc6, 0xf5, 0xcf, 0x30, 0xe8, 0xc1, 0x9a, 0x5a, 0xb4, 0x26, 0x5a, 0x04, + 0x2d, 0x08, 0xc6, 0x85, 0xf5, 0xa2, 0x3c, 0x65, 0x4f, 0xcc, 0xb6, 0x47, 0xb0, 0x3f, 0xa0, 0x2f, + 0x40, 0xed, 0x38, 0x87, 0x49, 0x77, 0xfe, 0xaa, 0xc2, 0xcc, 0x4e, 0xb7, 0x6d, 0x31, 0x79, 0x2e, + 0xcf, 0x1b, 0xf1, 0xc8, 0xb7, 0x4d, 0xd7, 0xa1, 0x48, 0x7d, 0x1c, 0xf2, 0x42, 0x49, 0x26, 0xf1, + 0x02, 0xef, 0x13, 0x57, 0x49, 0x68, 0x09, 0x0a, 0xc1, 0x14, 0xcf, 0x66, 0x9c, 0x78, 0x0d, 0x83, + 0x9c, 0xe1, 0xd9, 0x0c, 0xfd, 0x0d, 0xae, 0xd8, 0x5e, 0xc7, 0x70, 0x9d, 0xe7, 0xd4, 0xe8, 0x12, + 0xd7, 0xe0, 0x96, 0x8d, 0xae, 0xe9, 0x32, 0x1e, 0xd6, 0x34, 0x3c, 0x6b, 0x7b, 0x1d, 0xec, 0x3c, + 0xa7, 0x8f, 0x89, 0xcb, 0x17, 0x7f, 0x6c, 0xba, 0x0c, 0xfd, 0x17, 0xf2, 0x66, 0x7b, 0xdf, 0x71, + 0x2d, 0x76, 0xd0, 0x91, 0x37, 0x48, 0xba, 0x84, 0x79, 0x84, 0x99, 0xfa, 0xbd, 0x60, 0x26, 0x8e, + 0x5e, 0x42, 0xb7, 0x00, 0x79, 0x94, 0x18, 0x02, 0x9c, 0x58, 0xb4, 0xb7, 0x2e, 0xaf, 0x93, 0xa6, + 0x3c, 0x4a, 0x22, 0x33, 0x4f, 0xd6, 0xf5, 0x1f, 0x34, 0x40, 0x71, 0xbb, 0x32, 0x2e, 0xfd, 0x13, + 0x32, 0xfc, 0x7d, 0x5a, 0x55, 0xb8, 0x27, 0x97, 0xc2, 0x53, 0x79, 0x64, 0x6e, 0xdd, 0x87, 0x8d, + 0xe5, 0xf4, 0xda, 0x53, 0x28, 0x06, 0xea, 0xe4, 0xdb, 0x89, 0x7b, 0x43, 0x19, 0x9a, 0x51, 0xd4, + 0x11, 0x32, 0x4a, 0xed, 0x3f, 0x90, 0xe7, 0x95, 0xcc, 0x89, 0xb6, 0xa3, 0xfa, 0x4b, 0x8d, 0xd7, + 0x5f, 0xb5, 0x9f, 0x15, 0x48, 0xf1, 0x97, 0x47, 0xfe, 0x15, 0xf7, 0x90, 0x17, 0xbe, 0x02, 0xa5, + 0xf0, 0x9e, 0x08, 0x54, 0x37, 0x86, 0x50, 0x12, 0xa7, 0x00, 0x17, 0x5b, 0x71, 0x42, 0x36, 0x01, + 0xc4, 0x67, 0x65, 0x6e, 0x4a, 0xe8, 0xf0, 0xcf, 0x43, 0x4c, 0x85, 0xdb, 0xc5, 0x79, 0x1a, 0xee, + 0x1c, 0x41, 0x8a, 0x5a, 0x1f, 0x8a, 0xc8, 0xa0, 0x61, 0xfe, 0xac, 0xdf, 0x85, 0xb9, 0xff, 0x11, + 0xb6, 0xe3, 0xf6, 0x82, 0xea, 0x23, 0x38, 0x3e, 0x43, 0x68, 0xd2, 0x31, 0xcc, 0x27, 0x5f, 0x92, + 0x0a, 0xf8, 0x17, 0x14, 0xa9, 0xdb, 0x33, 0xfa, 0xde, 0xf4, 0x33, 0x71, 0xe8, 0x9e, 0xf8, 0x4b, + 0x05, 0x1a, 0x35, 0xf4, 0xaf, 0x54, 0x98, 0x7d, 0xab, 0xdb, 0x34, 0xd9, 0xa4, 0xc7, 0xcc, 0x31, + 0xcb, 0x93, 0x05, 0xc8, 0x33, 0xab, 0x43, 0x28, 0x33, 0x3b, 0x5d, 0x79, 0x92, 0xa3, 0x0e, 0x5f, + 0x57, 0xa4, 0x47, 0x6c, 0x26, 0x6f, 0xd2, 0x02, 0x5d, 0x6d, 0xf9, 0x7d, 0xbb, 0x4e, 0x8b, 0xd8, + 0x58, 0x8c, 0xeb, 0x2d, 0xa8, 0xf4, 0xb3, 0x24, 0x89, 0x5f, 0x0d, 0x0c, 0xf4, 0x57, 0x2a, 0xb2, + 0xc0, 0xf1, 0x47, 0xa4, 0x05, 0x74, 0x13, 0xa6, 0xfd, 0x92, 0xa5, 0x43, 0x8c, 0x08, 0x8f, 0xf8, + 0xd4, 0x3f, 0x25, 0xfa, 0x77, 0x83, 0xee, 0x8d, 0x1a, 0x54, 0x1b, 0x4e, 0xa7, 0x7e, 0xe8, 0x78, + 0xcc, 0xdb, 0x23, 0xf5, 0x9e, 0xc5, 0x08, 0xa5, 0xe2, 0x7f, 0x23, 0xf6, 0x32, 0xfc, 0xcf, 0xdd, + 0x3f, 0x02, 0x00, 0x00, 0xff, 0xff, 0xe6, 0x1e, 0x7e, 0xf1, 0x64, 0x21, 0x00, 0x00, } diff --git a/go/vt/proto/vtgateservice/vtgateservice.pb.go b/go/vt/proto/vtgateservice/vtgateservice.pb.go index af61880068e..a3acda6d7c8 100644 --- a/go/vt/proto/vtgateservice/vtgateservice.pb.go +++ b/go/vt/proto/vtgateservice/vtgateservice.pb.go @@ -1045,7 +1045,7 @@ func init() { proto.RegisterFile("vtgateservice.proto", fileDescriptor0) } var fileDescriptor0 = []byte{ // 551 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x09, 0x6e, 0x88, 0x02, 0xff, 0x84, 0x95, 0x5f, 0x6f, 0xd3, 0x30, + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x84, 0x95, 0x5f, 0x6f, 0xd3, 0x30, 0x14, 0xc5, 0xe1, 0x81, 0x82, 0x2e, 0x2d, 0x42, 0x1e, 0x74, 0x5b, 0xd9, 0x18, 0x2b, 0x62, 0xe3, 0x29, 0x42, 0x20, 0x21, 0x21, 0x21, 0xa1, 0x16, 0x2a, 0x84, 0xa6, 0x01, 0x6b, 0xf9, 0xf3, 0xc4, 0x83, 0x9b, 0x5e, 0x65, 0x51, 0xd3, 0x24, 0x8d, 0x9d, 0x88, 0x7e, 0x65, 0x3e, 0x05, 0x5a, 0x62, diff --git a/go/vt/proto/vtrpc/vtrpc.pb.go b/go/vt/proto/vtrpc/vtrpc.pb.go index f88edca9057..e0d5b77a9a1 100644 --- a/go/vt/proto/vtrpc/vtrpc.pb.go +++ b/go/vt/proto/vtrpc/vtrpc.pb.go @@ -384,7 +384,7 @@ func init() { proto.RegisterFile("vtrpc.proto", fileDescriptor0) } var fileDescriptor0 = []byte{ // 590 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x09, 0x6e, 0x88, 0x02, 0xff, 0x64, 0x93, 0x4d, 0x4f, 0xdb, 0x40, + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x64, 0x93, 0x4d, 0x4f, 0xdb, 0x40, 0x10, 0x86, 0xc9, 0x07, 0xf9, 0x18, 0x07, 0xb2, 0x0c, 0x5f, 0x81, 0x52, 0xb5, 0xe2, 0x54, 0x71, 0xc8, 0xa1, 0x3d, 0xf4, 0xbc, 0xf1, 0x0e, 0x61, 0x85, 0x59, 0xa7, 0xeb, 0x35, 0x25, 0xa7, 0x55, 0x08, 0x16, 0xa2, 0x0a, 0x38, 0x72, 0x02, 0x12, 0x97, 0xfe, 0xac, 0xfe, 0xa6, 0xfe, 0x8c, 0x6a, diff --git a/go/vt/proto/vttest/vttest.pb.go b/go/vt/proto/vttest/vttest.pb.go index 18bb04df568..e2390901ec8 100644 --- a/go/vt/proto/vttest/vttest.pb.go +++ b/go/vt/proto/vttest/vttest.pb.go @@ -170,7 +170,7 @@ func init() { proto.RegisterFile("vttest.proto", fileDescriptor0) } var fileDescriptor0 = []byte{ // 297 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x09, 0x6e, 0x88, 0x02, 0xff, 0x6c, 0x51, 0xcd, 0x6a, 0xf3, 0x30, + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x6c, 0x51, 0xcd, 0x6a, 0xf3, 0x30, 0x10, 0xc4, 0x49, 0xec, 0xef, 0xcb, 0xe6, 0x87, 0x20, 0x72, 0xd0, 0xad, 0x69, 0x4a, 0xc1, 0xa7, 0x50, 0xda, 0x47, 0x08, 0xed, 0xa5, 0xd0, 0x82, 0x6b, 0x72, 0x35, 0x8e, 0xb5, 0x4d, 0x4d, 0x65, 0x4b, 0x48, 0x8a, 0xc1, 0xaf, 0xd1, 0x27, 0x2e, 0x5e, 0xcb, 0xf4, 0xe2, 0xdb, 0x68, 0x66, 0x76, diff --git a/go/vt/proto/vtworkerdata/vtworkerdata.pb.go b/go/vt/proto/vtworkerdata/vtworkerdata.pb.go index c15c4236931..b8299e59eee 100644 --- a/go/vt/proto/vtworkerdata/vtworkerdata.pb.go +++ b/go/vt/proto/vtworkerdata/vtworkerdata.pb.go @@ -73,7 +73,7 @@ func init() { proto.RegisterFile("vtworkerdata.proto", fileDescriptor0) } var fileDescriptor0 = []byte{ // 147 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x09, 0x6e, 0x88, 0x02, 0xff, 0xe2, 0x12, 0x2a, 0x2b, 0x29, 0xcf, + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0x12, 0x2a, 0x2b, 0x29, 0xcf, 0x2f, 0xca, 0x4e, 0x2d, 0x4a, 0x49, 0x2c, 0x49, 0xd4, 0x2b, 0x28, 0xca, 0x2f, 0xc9, 0x17, 0xe2, 0x41, 0x16, 0x93, 0xe2, 0xcd, 0xc9, 0x4f, 0x2f, 0x2d, 0xc9, 0xcc, 0x81, 0x48, 0x2a, 0x19, 0x73, 0xc9, 0xba, 0x56, 0xa4, 0x26, 0x97, 0x96, 0xa4, 0x86, 0x41, 0x55, 0x39, 0xe7, 0xe7, 0xe6, 0x26, diff --git a/go/vt/proto/vtworkerservice/vtworkerservice.pb.go b/go/vt/proto/vtworkerservice/vtworkerservice.pb.go index 1ffc24c794b..75cf04ee7f9 100644 --- a/go/vt/proto/vtworkerservice/vtworkerservice.pb.go +++ b/go/vt/proto/vtworkerservice/vtworkerservice.pb.go @@ -140,7 +140,7 @@ func init() { proto.RegisterFile("vtworkerservice.proto", fileDescriptor0) } var fileDescriptor0 = []byte{ // 123 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x09, 0x6e, 0x88, 0x02, 0xff, 0xe2, 0x12, 0x2d, 0x2b, 0x29, 0xcf, + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0x12, 0x2d, 0x2b, 0x29, 0xcf, 0x2f, 0xca, 0x4e, 0x2d, 0x2a, 0x4e, 0x2d, 0x2a, 0xcb, 0x4c, 0x4e, 0xd5, 0x2b, 0x28, 0xca, 0x2f, 0xc9, 0x17, 0xe2, 0x47, 0x13, 0x96, 0x12, 0x82, 0x09, 0xa4, 0x24, 0x96, 0x24, 0x42, 0x14, 0x19, 0x35, 0x33, 0x72, 0x71, 0x84, 0x41, 0x85, 0x85, 0xca, 0xb9, 0xc4, 0x5c, 0x2b, 0x52, 0x93, 0x4b, diff --git a/go/vt/proto/workflow/workflow.pb.go b/go/vt/proto/workflow/workflow.pb.go index cb3cf7a64d6..2a93fa89680 100644 --- a/go/vt/proto/workflow/workflow.pb.go +++ b/go/vt/proto/workflow/workflow.pb.go @@ -272,7 +272,7 @@ func init() { proto.RegisterFile("workflow.proto", fileDescriptor0) } var fileDescriptor0 = []byte{ // 477 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x09, 0x6e, 0x88, 0x02, 0xff, 0x94, 0x53, 0xdb, 0x6e, 0xd3, 0x40, + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x94, 0x53, 0xdb, 0x6e, 0xd3, 0x40, 0x10, 0x65, 0x7d, 0x69, 0x9c, 0x71, 0xea, 0x46, 0x43, 0x25, 0x4c, 0x24, 0x90, 0x89, 0x90, 0x30, 0x91, 0xc8, 0x43, 0x90, 0x10, 0x02, 0xb5, 0x12, 0xe2, 0x22, 0x9e, 0xfa, 0xe0, 0x54, 0xf0, 0x18, 0x6d, 0xe3, 0x6d, 0x59, 0xa5, 0xd9, 0xad, 0xd6, 0xeb, 0x56, 0xf9, 0x40, 0x7e, 0x81, 0x6f, 0xe0, diff --git a/go/vt/sqlannotation/sqlannotation.go b/go/vt/sqlannotation/sqlannotation.go index a6ffa4baa96..3d8cbb2d75d 100644 --- a/go/vt/sqlannotation/sqlannotation.go +++ b/go/vt/sqlannotation/sqlannotation.go @@ -16,7 +16,6 @@ import ( "fmt" "log" "strings" - "unicode" "bytes" @@ -39,7 +38,7 @@ var ( // it is used to annotate 'sql' // Otherwise 'sql' is annotated as replication-unfriendly. func AnnotateIfDML(sql string, keyspaceIDs [][]byte) string { - if !IsDML(sql) { + if !sqlparser.IsDML(sql) { return sql } if len(keyspaceIDs) == 1 { @@ -62,19 +61,6 @@ func AddKeyspaceIDs(sql string, keyspaceIDs [][]byte, trailingComments string) s sql, bytes.Join(encodedIDs, []byte(",")), trailingComments) } -// IsDML returns true if 'querySQL' is an INSERT, UPDATE or DELETE statement. -func IsDML(sql string) bool { - sql = strings.TrimLeftFunc(sql, unicode.IsSpace) - end := strings.IndexFunc(sql, unicode.IsSpace) - if end == -1 { - return false - } - word := sql[:end] - return strings.EqualFold(word, "insert") || - strings.EqualFold(word, "update") || - strings.EqualFold(word, "delete") -} - // ExtractKeyspaceIDS parses the annotation of the given statement and tries // to extract the keyspace id. // If a keyspace-id comment exists 'keyspaceID' is set to the parsed keyspace id diff --git a/go/vt/sqlannotation/sqlannotation_test.go b/go/vt/sqlannotation/sqlannotation_test.go index ad38cdae903..43518b13c63 100644 --- a/go/vt/sqlannotation/sqlannotation_test.go +++ b/go/vt/sqlannotation/sqlannotation_test.go @@ -50,28 +50,6 @@ func verifyParseError(t *testing.T, sql string) { } } -var IsDMLTests = []struct { - sql string - isDML bool -}{ - {" update ...", true}, - {"UPDATE ...", true}, - {"\n\t delete ...", true}, - {"insert ...", true}, - {"select ...", false}, - {" select ...", false}, - {"", false}, - {" ", false}, -} - -func TestIsDML(t *testing.T) { - for _, test := range IsDMLTests { - if dml := IsDML(test.sql); dml != test.isDML { - t.Errorf("IsDML(%q) = %t, got %t", test.sql, dml, test.isDML) - } - } -} - func BenchmarkExtractKeyspaceIDKeyspaceID(b *testing.B) { for i := 0; i < b.N; i++ { ExtractKeyspaceIDS("DML /* vtgate:: keyspace_id:25AF */") @@ -96,12 +74,6 @@ func BenchmarkExtractKeySpaceIDNothing(b *testing.B) { } } -func BenchmarkIsDML(b *testing.B) { - for i := 0; i < b.N; i++ { - IsDML("UPDATE ultimatequestion set answer=42 where question = 'What do you get if you multiply six by nine?'") - } -} - func TestAddKeyspaceIDs(t *testing.T) { ksid1 := []byte{0x37} ksid2 := []byte{0x29} diff --git a/go/vt/sqlparser/analyzer.go b/go/vt/sqlparser/analyzer.go index 02f26914a91..daca46ce09c 100644 --- a/go/vt/sqlparser/analyzer.go +++ b/go/vt/sqlparser/analyzer.go @@ -8,6 +8,9 @@ package sqlparser import ( "fmt" + "strconv" + "strings" + "unicode" "github.com/youtube/vitess/go/sqltypes" ) @@ -129,3 +132,68 @@ func StringIn(str string, values ...string) bool { } return false } + +// ExtractSetNums returns a map of key-num pairs +// if the query is a SET statement. Otherwise, it returns an +// error. +func ExtractSetNums(sql string) (map[string]int64, error) { + stmt, err := Parse(sql) + if err != nil { + return nil, err + } + setStmt, ok := stmt.(*Set) + if !ok { + return nil, fmt.Errorf("ast did not yield *sqlparser.Set: %T", stmt) + } + result := make(map[string]int64) + for _, expr := range setStmt.Exprs { + if expr.Name.Qualifier != nil { + return nil, fmt.Errorf("invalid syntax: %v", String(expr.Name)) + } + key := expr.Name.Name.Lowered() + + sqlval, ok := expr.Expr.(*SQLVal) + if !ok { + return nil, fmt.Errorf("invalid syntax: %s", String(expr.Expr)) + } + if sqlval.Type != IntVal { + return nil, fmt.Errorf("invalid value type: %v", String(expr.Expr)) + } + num, err := strconv.ParseInt(string(sqlval.Val), 0, 64) + if err != nil { + return nil, err + } + result[key] = num + } + return result, nil +} + +// HasPrefix returns true if the query has one of the specified +// statement prefixes. For example, you can find out if a query +// is a DML with HasPrefix(sql, "insert", "update", "delete"). +func HasPrefix(sql string, values ...string) bool { + sql = strings.TrimLeftFunc(sql, unicode.IsSpace) + end := strings.IndexFunc(sql, unicode.IsSpace) + word := sql + if end != -1 { + word = sql[:end] + } + for _, val := range values { + if strings.EqualFold(word, val) { + return true + } + } + return false +} + +// IsDML returns true if the query is an INSERT, UPDATE or DELETE statement. +func IsDML(sql string) bool { + return HasPrefix(sql, "insert", "update", "delete") +} + +// IsStatement returns true if the sql matches a single-word +// statement like begin, commit or rollback. +func IsStatement(sql, statement string) bool { + sql = strings.TrimFunc(sql, unicode.IsSpace) + return strings.EqualFold(sql, statement) +} diff --git a/go/vt/sqlparser/analyzer_test.go b/go/vt/sqlparser/analyzer_test.go index 8e683b3e65c..d5aab68bc9f 100644 --- a/go/vt/sqlparser/analyzer_test.go +++ b/go/vt/sqlparser/analyzer_test.go @@ -198,6 +198,93 @@ func TestStringIn(t *testing.T) { } } +func TestExtractSetNums(t *testing.T) { + testcases := []struct { + sql string + out map[string]int64 + err string + }{{ + sql: "invalid", + err: "syntax error at position 8 near 'invalid'", + }, { + sql: "select * from t", + err: "ast did not yield *sqlparser.Set: *sqlparser.Select", + }, { + sql: "set a.autocommit=1", + err: "invalid syntax: a.autocommit", + }, { + sql: "set autocommit=1+1", + err: "invalid syntax: 1 + 1", + }, { + sql: "set autocommit='aa'", + err: "invalid value type: 'aa'", + }, { + sql: "set autocommit=1", + out: map[string]int64{"autocommit": 1}, + }, { + sql: "set AUTOCOMMIT=1", + out: map[string]int64{"autocommit": 1}, + }} + for _, tcase := range testcases { + out, err := ExtractSetNums(tcase.sql) + if tcase.err != "" { + if err == nil || err.Error() != tcase.err { + t.Errorf("ExtractSetNums(%s): %v, want '%s'", tcase.sql, err, tcase.err) + } + } else if err != nil { + t.Errorf("ExtractSetNums(%s): %v, want no error", tcase.sql, err) + } + if !reflect.DeepEqual(out, tcase.out) { + t.Errorf("ExtractSetNums(%s): %v, want '%v'", tcase.sql, out, tcase.out) + } + } +} + +func TestHasPrefix(t *testing.T) { + testcases := []struct { + sql string + want bool + }{ + {" update ...", true}, + {"Update", true}, + {"UPDATE ...", true}, + {"\n\t delete ...", true}, + {"insert ...", true}, + {"select ...", false}, + {" select ...", false}, + {"", false}, + {" ", false}, + } + for _, tcase := range testcases { + if got := HasPrefix(tcase.sql, "insert", "update", "delete"); got != tcase.want { + t.Errorf("HasPrefix(%s): %v, want %v", tcase.sql, got, tcase.want) + } + if got := IsDML(tcase.sql); got != tcase.want { + t.Errorf("IsDML(%s): %v, want %v", tcase.sql, got, tcase.want) + } + } +} + +func TestIsStatement(t *testing.T) { + testcases := []struct { + sql string + want bool + }{ + {"begin", true}, + {" begin", true}, + {" begin ", true}, + {"begin ", true}, + {"\n\t begin ", true}, + {"... begin", false}, + {"begin ...", false}, + } + for _, tcase := range testcases { + if got := IsStatement(tcase.sql, "begin"); got != tcase.want { + t.Errorf("IsStatement(%s): %v, want %v", tcase.sql, got, tcase.want) + } + } +} + func newStrVal(in string) *SQLVal { return NewStrVal([]byte(in)) } diff --git a/go/vt/sqlparser/ast.go b/go/vt/sqlparser/ast.go index 33da94ff4c9..ce1952f0354 100644 --- a/go/vt/sqlparser/ast.go +++ b/go/vt/sqlparser/ast.go @@ -108,6 +108,7 @@ func (*Update) iStatement() {} func (*Delete) iStatement() {} func (*Set) iStatement() {} func (*DDL) iStatement() {} +func (*Show) iStatement() {} func (*Other) iStatement() {} // SelectStatement any SELECT statement. @@ -435,7 +436,31 @@ func (node *DDL) WalkSubtree(visit Visit) error { ) } -// Other represents a SHOW, DESCRIBE, or EXPLAIN statement. +// Show represents a show statement. +type Show struct { + Type string +} + +const ( + ShowDatabasesStr = "show databases" + ShowKeyspacesStr = "show vitess_keyspaces" + ShowShardsStr = "show vitess_shards" + ShowTablesStr = "show tables" + ShowVSchemaTablesStr = "show vschema_tables" + ShowUnsupportedStr = "show unsupported" +) + +// Format formats the node. +func (node *Show) Format(buf *TrackedBuffer) { + buf.Myprintf("%s", node.Type) +} + +// WalkSubtree walks the nodes of the subtree. +func (node *Show) WalkSubtree(visit Visit) error { + return nil +} + +// Other represents a DESCRIBE, or EXPLAIN statement. // It should be used only as an indicator. It does not contain // the full AST for the statement. type Other struct{} diff --git a/go/vt/sqlparser/parse_test.go b/go/vt/sqlparser/parse_test.go index c110be09108..435e9ccb448 100644 --- a/go/vt/sqlparser/parse_test.go +++ b/go/vt/sqlparser/parse_test.go @@ -623,6 +623,12 @@ func TestValid(t *testing.T) { }, { input: "create unique index a using foo on b", output: "alter table b", + }, { + input: "create fulltext index a using foo on b", + output: "alter table b", + }, { + input: "create spatial index a using foo on b", + output: "alter table b", }, { input: "create view a", output: "create table a", @@ -647,9 +653,27 @@ func TestValid(t *testing.T) { }, { input: "analyze table a", output: "alter table a", + }, { + input: "show databases", + output: "show databases", + }, { + input: "show tables", + output: "show tables", + }, { + input: "show vitess_keyspaces", + output: "show vitess_keyspaces", + }, { + input: "show vitess_shards", + output: "show vitess_shards", + }, { + input: "show vschema_tables", + output: "show vschema_tables", + }, { + input: "show create database", + output: "show unsupported", }, { input: "show foobar", - output: "other", + output: "show unsupported", }, { input: "describe foobar", output: "other", diff --git a/go/vt/sqlparser/sql.go b/go/vt/sqlparser/sql.go index db61dff4665..22e5483e446 100644 --- a/go/vt/sqlparser/sql.go +++ b/go/vt/sqlparser/sql.go @@ -171,30 +171,35 @@ const DESCRIBE = 57445 const EXPLAIN = 57446 const DATE = 57447 const ESCAPE = 57448 -const INTEGER = 57449 -const CHARACTER = 57450 -const CURRENT_TIMESTAMP = 57451 -const DATABASE = 57452 -const CURRENT_DATE = 57453 -const CURRENT_TIME = 57454 -const LOCALTIME = 57455 -const LOCALTIMESTAMP = 57456 -const UTC_DATE = 57457 -const UTC_TIME = 57458 -const UTC_TIMESTAMP = 57459 -const REPLACE = 57460 -const CONVERT = 57461 -const CAST = 57462 -const GROUP_CONCAT = 57463 -const SEPARATOR = 57464 -const MATCH = 57465 -const AGAINST = 57466 -const BOOLEAN = 57467 -const LANGUAGE = 57468 -const WITH = 57469 -const QUERY = 57470 -const EXPANSION = 57471 -const UNUSED = 57472 +const DATABASES = 57449 +const TABLES = 57450 +const VITESS_KEYSPACES = 57451 +const VITESS_SHARDS = 57452 +const VSCHEMA_TABLES = 57453 +const INTEGER = 57454 +const CHARACTER = 57455 +const CURRENT_TIMESTAMP = 57456 +const DATABASE = 57457 +const CURRENT_DATE = 57458 +const CURRENT_TIME = 57459 +const LOCALTIME = 57460 +const LOCALTIMESTAMP = 57461 +const UTC_DATE = 57462 +const UTC_TIME = 57463 +const UTC_TIMESTAMP = 57464 +const REPLACE = 57465 +const CONVERT = 57466 +const CAST = 57467 +const GROUP_CONCAT = 57468 +const SEPARATOR = 57469 +const MATCH = 57470 +const AGAINST = 57471 +const BOOLEAN = 57472 +const LANGUAGE = 57473 +const WITH = 57474 +const QUERY = 57475 +const EXPANSION = 57476 +const UNUSED = 57477 var yyToknames = [...]string{ "$end", @@ -320,6 +325,11 @@ var yyToknames = [...]string{ "EXPLAIN", "DATE", "ESCAPE", + "DATABASES", + "TABLES", + "VITESS_KEYSPACES", + "VITESS_SHARDS", + "VSCHEMA_TABLES", "INTEGER", "CHARACTER", "CURRENT_TIMESTAMP", @@ -357,357 +367,377 @@ var yyExca = [...]int{ -1, 1, 1, -1, -2, 0, - -1, 84, - 103, 306, - -2, 302, - -1, 85, - 103, 307, - -2, 303, - -1, 295, - 103, 309, - -2, 305, + -1, 180, + 103, 311, + -2, 307, + -1, 181, + 103, 312, + -2, 308, + -1, 306, + 103, 314, + -2, 310, } -const yyNprod = 413 +const yyNprod = 423 const yyPrivate = 57344 var yyTokenNames []string var yyStates []string -const yyLast = 3133 +const yyLast = 3400 var yyAct = [...]int{ - 424, 247, 432, 331, 596, 270, 79, 377, 121, 505, - 475, 525, 534, 563, 595, 420, 271, 249, 503, 314, - 243, 411, 490, 417, 504, 444, 119, 294, 312, 61, - 519, 98, 246, 28, 687, 49, 124, 305, 81, 237, - 62, 680, 83, 244, 686, 62, 673, 685, 62, 679, - 62, 303, 376, 3, 62, 672, 599, 640, 468, 477, - 469, 50, 51, 299, 37, 62, 39, 109, 94, 89, - 40, 87, 91, 42, 92, 43, 307, 468, 97, 469, - 43, 406, 52, 213, 62, 330, 72, 329, 328, 104, - 88, 90, 62, 80, 48, 67, 62, 44, 578, 62, - 366, 367, 62, 575, 74, 71, 470, 81, 112, 62, - 427, 83, 228, 415, 109, 81, 116, 235, 212, 83, - 211, 235, 125, 214, 293, 470, 217, 471, 356, 401, - 302, 304, 301, 227, 240, 375, 45, 46, 47, 234, - 230, 232, 107, 296, 115, 111, 471, 346, 236, 668, - 356, 671, 295, 543, 336, 345, 344, 354, 355, 347, - 348, 349, 350, 351, 352, 353, 346, 335, 334, 356, - 306, 675, 451, 335, 334, 122, 334, 491, 468, 114, - 469, 602, 76, 491, 336, 551, 449, 450, 448, 117, - 336, 66, 336, 69, 662, 345, 344, 354, 355, 347, - 348, 349, 350, 351, 352, 353, 346, 239, 231, 356, - 65, 122, 70, 77, 73, 68, 75, 101, 269, 62, - 544, 447, 62, 669, 315, 650, 470, 81, 527, 528, - 529, 83, 324, 354, 355, 347, 348, 349, 350, 351, - 352, 353, 346, 308, 310, 356, 309, 471, 118, 632, - 635, 233, 426, 663, 654, 82, 659, 426, 333, 438, - 440, 441, 337, 364, 439, 561, 426, 238, 327, 587, - 588, 368, 369, 370, 371, 372, 373, 323, 311, 345, - 344, 354, 355, 347, 348, 349, 350, 351, 352, 353, - 346, 95, 378, 356, 113, 584, 332, 545, 30, 386, - 480, 349, 350, 351, 352, 353, 346, 113, 123, 356, - 123, 335, 334, 335, 334, 418, 535, 113, 221, 585, - 604, 561, 62, 408, 82, 408, 426, 122, 336, 583, - 336, 428, 82, 408, 123, 123, 404, 580, 426, 421, - 514, 335, 334, 335, 334, 426, 416, 93, 423, 547, - 426, 480, 426, 409, 435, 436, 445, 446, 336, 443, - 336, 467, 452, 453, 454, 455, 456, 457, 458, 459, - 460, 461, 462, 463, 464, 465, 466, 479, 481, 645, - 315, 235, 442, 418, 478, 315, 315, 402, 388, 219, - 493, 425, 426, 216, 408, 96, 225, 648, 647, 472, - 473, 484, 485, 315, 315, 315, 315, 613, 62, 495, - 498, 62, 295, 612, 315, 62, 488, 122, 272, 684, - 616, 235, 683, 515, 478, 617, 678, 508, 614, 56, - 57, 518, 499, 615, 510, 500, 123, 502, 681, 482, - 483, 41, 378, 512, 82, 326, 618, 501, 569, 570, - 507, 100, 295, 523, 526, 14, 665, 494, 320, 496, - 497, 521, 522, 319, 99, 445, 446, 513, 666, 676, - 531, 532, 533, 651, 530, 59, 537, 105, 576, 297, - 322, 215, 315, 541, 542, 60, 573, 546, 53, 54, - 100, 318, 433, 553, 315, 554, 555, 556, 557, 317, - 540, 263, 262, 264, 265, 266, 267, 607, 120, 268, - 120, 552, 550, 434, 332, 62, 606, 560, 403, 238, - 579, 226, 581, 582, 31, 78, 558, 508, 574, 674, - 577, 421, 623, 413, 14, 120, 538, 30, 32, 593, - 33, 34, 35, 36, 589, 315, 27, 1, 548, 572, - 594, 106, 601, 410, 298, 38, 591, 592, 405, 597, - 598, 300, 86, 316, 224, 325, 486, 62, 62, 62, - 62, 603, 609, 664, 611, 621, 586, 524, 620, 508, - 508, 508, 508, 605, 498, 619, 608, 559, 610, 549, - 385, 489, 474, 248, 123, 630, 631, 628, 636, 637, - 419, 634, 487, 626, 627, 526, 492, 437, 259, 642, - 256, 629, 258, 257, 220, 644, 321, 338, 241, 229, - 123, 102, 564, 562, 641, 81, 643, 378, 506, 83, - 649, 413, 407, 639, 123, 661, 120, 55, 218, 29, - 653, 58, 13, 12, 516, 655, 517, 11, 520, 520, - 520, 658, 10, 9, 8, 7, 660, 6, 5, 652, - 4, 2, 670, 667, 365, 656, 657, 0, 0, 0, - 677, 390, 391, 392, 393, 394, 395, 396, 0, 0, - 0, 682, 0, 0, 85, 0, 0, 0, 374, 0, - 0, 0, 379, 380, 381, 382, 383, 384, 0, 387, - 389, 389, 389, 389, 389, 389, 389, 389, 397, 398, - 399, 400, 14, 15, 16, 17, 0, 0, 123, 64, - 0, 0, 0, 0, 64, 0, 0, 64, 0, 64, - 590, 0, 0, 64, 0, 18, 565, 568, 569, 570, - 566, 422, 567, 571, 64, 0, 64, 429, 430, 431, - 345, 344, 354, 355, 347, 348, 349, 350, 351, 352, - 353, 346, 0, 64, 356, 0, 600, 0, 0, 0, - 0, 64, 0, 0, 126, 64, 126, 0, 64, 0, - 0, 64, 0, 0, 126, 0, 0, 0, 64, 0, - 0, 0, 0, 64, 123, 0, 64, 624, 0, 625, - 0, 126, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 19, 20, 22, 21, 23, 0, 0, 0, - 0, 0, 0, 0, 0, 24, 25, 26, 509, 0, - 0, 72, 0, 0, 476, 0, 245, 0, 0, 0, - 67, 0, 82, 0, 0, 280, 0, 0, 0, 74, - 71, 0, 0, 0, 0, 273, 274, 0, 0, 0, - 0, 0, 0, 0, 122, 536, 0, 84, 263, 262, - 264, 265, 266, 267, 0, 0, 268, 260, 261, 0, - 0, 242, 254, 0, 279, 345, 344, 354, 355, 347, - 348, 349, 350, 351, 352, 353, 346, 539, 64, 356, - 0, 64, 126, 0, 251, 252, 313, 0, 0, 0, - 291, 126, 253, 0, 0, 250, 255, 0, 120, 347, - 348, 349, 350, 351, 352, 353, 346, 76, 509, 356, - 289, 0, 0, 422, 0, 0, 66, 0, 69, 0, - 281, 290, 287, 288, 285, 286, 284, 283, 282, 292, - 275, 276, 278, 0, 277, 65, 0, 70, 77, 73, - 68, 75, 345, 344, 354, 355, 347, 348, 349, 350, - 351, 352, 353, 346, 0, 0, 356, 0, 0, 0, - 509, 509, 509, 509, 126, 344, 354, 355, 347, 348, - 349, 350, 351, 352, 353, 346, 0, 0, 356, 126, - 0, 64, 72, 0, 0, 0, 412, 0, 0, 0, - 0, 67, 0, 0, 0, 633, 0, 14, 638, 0, - 74, 71, 0, 565, 568, 569, 570, 566, 72, 567, - 571, 0, 0, 646, 0, 0, 0, 67, 125, 0, - 414, 0, 0, 0, 72, 0, 74, 71, 108, 0, - 335, 334, 0, 67, 0, 0, 0, 0, 126, 0, - 0, 122, 74, 71, 63, 0, 0, 336, 126, 0, - 0, 0, 126, 0, 0, 0, 0, 0, 0, 0, - 63, 0, 110, 72, 0, 0, 126, 64, 0, 0, - 64, 0, 67, 0, 64, 0, 0, 126, 76, 0, - 0, 74, 71, 0, 0, 0, 0, 66, 0, 69, - 126, 0, 126, 0, 126, 126, 126, 0, 0, 125, - 0, 0, 222, 0, 76, 223, 65, 0, 70, 77, - 73, 68, 75, 66, 0, 69, 0, 0, 0, 0, - 76, 0, 0, 0, 0, 0, 0, 0, 0, 66, - 0, 69, 65, 0, 70, 77, 73, 68, 75, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 65, 0, - 70, 77, 73, 68, 75, 0, 0, 0, 0, 76, - 0, 0, 0, 0, 126, 0, 0, 0, 66, 0, - 69, 0, 0, 0, 64, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 65, 0, 70, - 77, 73, 68, 75, 0, 0, 0, 0, 0, 0, + 435, 443, 388, 342, 607, 281, 258, 175, 536, 606, + 486, 218, 574, 501, 516, 545, 282, 431, 260, 325, + 254, 248, 323, 514, 515, 216, 455, 305, 257, 159, + 176, 422, 530, 29, 195, 428, 221, 488, 387, 3, + 255, 171, 204, 179, 177, 698, 171, 691, 697, 316, + 684, 696, 690, 683, 610, 651, 310, 160, 190, 399, + 241, 243, 67, 314, 171, 479, 171, 480, 183, 161, + 171, 673, 356, 355, 365, 366, 358, 359, 360, 361, + 362, 363, 364, 357, 59, 417, 367, 219, 318, 224, + 479, 479, 480, 480, 554, 194, 356, 355, 365, 366, + 358, 359, 360, 361, 362, 363, 364, 357, 346, 345, + 367, 186, 58, 481, 59, 61, 62, 63, 341, 211, + 340, 339, 589, 184, 170, 347, 64, 60, 242, 377, + 378, 586, 438, 546, 482, 674, 426, 412, 481, 481, + 367, 686, 313, 315, 312, 358, 359, 360, 361, 362, + 363, 364, 357, 386, 682, 367, 212, 208, 347, 482, + 482, 555, 613, 462, 502, 185, 643, 646, 679, 357, + 345, 502, 367, 562, 171, 214, 206, 460, 461, 459, + 171, 219, 189, 187, 198, 188, 347, 317, 171, 193, + 680, 458, 171, 661, 665, 171, 670, 437, 171, 360, + 361, 362, 363, 364, 357, 215, 171, 367, 179, 177, + 239, 206, 191, 223, 246, 437, 179, 177, 246, 420, + 598, 599, 210, 595, 304, 572, 437, 449, 451, 452, + 171, 251, 450, 171, 491, 326, 245, 219, 429, 307, + 210, 250, 179, 177, 335, 247, 192, 556, 244, 306, + 656, 346, 345, 596, 321, 419, 437, 594, 615, 280, + 419, 338, 346, 345, 346, 345, 31, 322, 347, 344, + 572, 346, 345, 348, 334, 375, 538, 539, 540, 347, + 42, 347, 379, 380, 381, 382, 383, 384, 347, 591, + 437, 346, 345, 201, 558, 437, 249, 178, 419, 209, + 491, 437, 343, 389, 436, 437, 525, 213, 347, 429, + 397, 222, 413, 437, 225, 227, 236, 228, 274, 273, + 275, 276, 277, 278, 230, 238, 279, 695, 576, 579, + 580, 581, 577, 171, 578, 582, 210, 219, 657, 419, + 627, 415, 439, 625, 629, 628, 580, 581, 626, 319, + 432, 659, 320, 401, 402, 403, 404, 405, 406, 407, + 658, 624, 623, 165, 166, 446, 447, 456, 694, 457, + 454, 434, 478, 463, 464, 465, 466, 467, 468, 469, + 470, 471, 472, 473, 474, 475, 476, 477, 490, 492, + 453, 326, 246, 689, 692, 197, 326, 326, 489, 518, + 331, 504, 57, 330, 587, 687, 483, 484, 196, 676, + 662, 15, 495, 496, 326, 326, 326, 326, 202, 171, + 506, 677, 171, 306, 509, 326, 171, 524, 493, 494, + 499, 283, 246, 308, 526, 169, 333, 168, 489, 519, + 512, 529, 513, 226, 584, 220, 505, 220, 507, 508, + 162, 163, 427, 389, 523, 521, 232, 197, 329, 444, + 618, 445, 178, 306, 534, 537, 328, 343, 617, 571, + 178, 249, 220, 220, 532, 533, 456, 174, 457, 685, + 634, 542, 543, 544, 33, 15, 541, 548, 220, 31, + 28, 1, 65, 326, 552, 553, 178, 337, 557, 583, + 203, 421, 309, 39, 564, 326, 565, 566, 567, 568, + 32, 551, 416, 311, 561, 182, 327, 576, 579, 580, + 581, 577, 563, 578, 582, 549, 171, 34, 35, 36, + 37, 590, 235, 592, 593, 336, 569, 559, 510, 519, + 497, 511, 432, 588, 585, 675, 597, 535, 616, 570, + 604, 560, 396, 500, 259, 430, 326, 448, 600, 270, + 267, 605, 269, 612, 268, 608, 609, 602, 603, 231, + 414, 332, 349, 252, 240, 199, 575, 573, 171, 171, + 171, 171, 614, 517, 418, 424, 632, 650, 620, 631, + 622, 519, 519, 519, 519, 630, 672, 619, 509, 621, + 164, 229, 30, 167, 14, 639, 641, 642, 13, 647, + 648, 12, 645, 11, 637, 638, 537, 217, 10, 217, + 653, 655, 640, 9, 8, 7, 6, 5, 4, 2, + 0, 237, 0, 0, 0, 652, 0, 654, 389, 0, + 179, 177, 660, 0, 485, 217, 220, 0, 0, 0, + 0, 664, 0, 0, 498, 666, 0, 0, 503, 0, + 217, 0, 669, 0, 0, 0, 0, 671, 0, 0, + 663, 0, 220, 681, 678, 0, 667, 668, 0, 0, + 0, 688, 0, 424, 0, 0, 220, 0, 376, 0, + 0, 0, 693, 0, 0, 0, 527, 0, 528, 0, + 531, 531, 531, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 385, 0, 0, 0, 390, 391, 392, 393, + 394, 395, 181, 398, 400, 400, 400, 400, 400, 400, + 400, 400, 408, 409, 410, 411, 0, 0, 52, 0, + 0, 0, 0, 44, 0, 0, 0, 47, 68, 0, + 0, 0, 0, 0, 0, 0, 54, 51, 173, 0, + 0, 0, 0, 173, 0, 433, 0, 0, 601, 0, + 220, 440, 441, 442, 43, 0, 0, 233, 0, 0, + 234, 173, 0, 173, 0, 0, 0, 173, 356, 355, + 365, 366, 358, 359, 360, 361, 362, 363, 364, 357, + 0, 0, 367, 0, 0, 0, 52, 0, 0, 0, + 0, 0, 0, 0, 0, 47, 0, 0, 611, 0, + 0, 0, 0, 0, 54, 51, 0, 0, 0, 0, + 0, 0, 0, 0, 158, 0, 0, 0, 0, 0, + 0, 0, 43, 46, 0, 0, 220, 0, 0, 635, + 49, 636, 520, 0, 356, 355, 365, 366, 358, 359, + 360, 361, 362, 363, 364, 357, 0, 45, 367, 50, + 56, 53, 48, 55, 0, 356, 355, 365, 366, 358, + 359, 360, 361, 362, 363, 364, 357, 0, 0, 367, + 0, 173, 0, 173, 178, 0, 0, 173, 0, 0, + 0, 0, 158, 0, 0, 173, 0, 0, 44, 173, + 44, 46, 173, 0, 0, 173, 0, 0, 49, 44, + 0, 550, 0, 173, 0, 0, 0, 0, 173, 0, + 0, 173, 0, 0, 0, 45, 44, 50, 56, 53, + 48, 55, 217, 0, 52, 0, 0, 173, 0, 256, + 173, 44, 520, 47, 0, 0, 0, 433, 291, 0, + 44, 0, 54, 51, 0, 0, 0, 0, 284, 285, + 0, 0, 0, 0, 0, 0, 0, 219, 0, 0, + 180, 274, 273, 275, 276, 277, 278, 0, 0, 279, + 271, 272, 0, 0, 253, 265, 0, 290, 0, 0, + 0, 0, 0, 0, 520, 520, 520, 520, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 262, 263, 324, + 0, 0, 0, 302, 0, 264, 0, 0, 261, 266, + 0, 0, 0, 44, 0, 0, 0, 0, 0, 644, + 158, 0, 649, 300, 0, 0, 0, 0, 44, 46, + 173, 0, 0, 0, 0, 0, 49, 0, 292, 301, + 298, 299, 296, 297, 295, 294, 293, 303, 286, 287, + 289, 0, 288, 45, 0, 50, 56, 53, 48, 55, + 15, 365, 366, 358, 359, 360, 361, 362, 363, 364, + 357, 52, 0, 367, 0, 0, 0, 0, 0, 0, + 47, 0, 0, 0, 0, 0, 0, 44, 0, 54, + 51, 0, 0, 0, 0, 0, 0, 44, 0, 0, + 0, 44, 0, 0, 219, 0, 0, 172, 0, 0, + 0, 0, 0, 52, 0, 44, 173, 0, 256, 173, + 0, 0, 47, 173, 0, 0, 44, 291, 0, 0, + 0, 54, 51, 0, 0, 0, 0, 284, 285, 44, + 0, 44, 0, 44, 44, 44, 219, 0, 437, 180, + 274, 273, 275, 276, 277, 278, 0, 0, 279, 271, + 272, 0, 0, 253, 265, 0, 290, 158, 15, 16, + 17, 18, 0, 0, 0, 0, 46, 0, 0, 0, + 0, 0, 0, 49, 0, 0, 262, 263, 0, 0, + 0, 19, 302, 0, 264, 0, 0, 261, 266, 0, + 45, 0, 50, 56, 53, 48, 55, 0, 0, 158, + 0, 0, 300, 44, 0, 0, 0, 0, 46, 0, + 0, 0, 0, 173, 0, 49, 0, 292, 301, 298, + 299, 296, 297, 295, 294, 293, 303, 286, 287, 289, + 0, 288, 45, 0, 50, 56, 53, 48, 55, 0, + 355, 365, 366, 358, 359, 360, 361, 362, 363, 364, + 357, 44, 0, 367, 0, 0, 0, 0, 20, 21, + 23, 22, 24, 0, 0, 173, 173, 173, 173, 0, + 0, 25, 26, 27, 0, 0, 173, 0, 0, 44, + 0, 0, 44, 0, 44, 147, 134, 108, 149, 86, + 100, 157, 101, 102, 128, 74, 116, 52, 98, 0, + 89, 70, 95, 71, 87, 110, 47, 113, 85, 136, + 119, 155, 0, 123, 0, 54, 51, 0, 0, 112, + 141, 114, 133, 107, 129, 79, 122, 150, 99, 126, + 0, 0, 0, 43, 0, 0, 0, 0, 0, 0, + 0, 0, 125, 146, 97, 127, 69, 124, 0, 72, + 75, 156, 144, 92, 93, 0, 0, 0, 0, 0, + 0, 0, 111, 115, 130, 105, 0, 0, 0, 0, + 0, 0, 633, 0, 90, 0, 121, 0, 0, 0, + 76, 73, 109, 0, 0, 0, 78, 0, 91, 131, + 0, 142, 106, 158, 145, 104, 103, 148, 151, 140, + 88, 96, 46, 94, 84, 143, 137, 138, 139, 49, + 0, 82, 83, 80, 81, 117, 118, 152, 153, 154, + 132, 77, 0, 0, 135, 120, 45, 0, 50, 56, + 53, 48, 55, 147, 134, 108, 149, 86, 100, 157, + 101, 102, 128, 74, 116, 52, 98, 0, 89, 70, + 95, 71, 87, 110, 47, 113, 85, 136, 119, 155, + 0, 123, 0, 54, 51, 0, 0, 112, 141, 114, + 133, 107, 129, 79, 122, 150, 99, 126, 219, 0, + 0, 43, 0, 0, 0, 0, 0, 0, 0, 0, + 125, 146, 97, 127, 69, 124, 0, 72, 75, 156, + 144, 92, 93, 0, 0, 0, 0, 0, 0, 0, + 111, 115, 130, 105, 0, 0, 0, 0, 0, 0, + 0, 0, 90, 0, 121, 0, 0, 0, 76, 73, + 109, 0, 0, 0, 78, 0, 91, 131, 0, 142, + 106, 158, 145, 104, 103, 148, 151, 140, 88, 96, + 46, 94, 84, 143, 137, 138, 139, 49, 0, 82, + 83, 80, 81, 117, 118, 152, 153, 154, 132, 77, + 0, 0, 135, 120, 45, 0, 50, 56, 53, 48, + 55, 147, 134, 108, 149, 86, 100, 157, 101, 102, + 128, 74, 116, 52, 98, 0, 89, 70, 95, 71, + 87, 110, 47, 113, 85, 136, 119, 155, 0, 123, + 0, 54, 51, 0, 0, 112, 141, 114, 133, 107, + 129, 79, 122, 150, 99, 126, 0, 0, 0, 180, + 0, 0, 0, 0, 0, 0, 0, 0, 125, 146, + 97, 127, 69, 124, 0, 72, 75, 156, 144, 92, + 93, 0, 0, 0, 0, 0, 0, 0, 111, 115, + 130, 105, 0, 0, 0, 0, 0, 0, 522, 0, + 90, 0, 121, 0, 0, 0, 76, 73, 109, 0, + 0, 0, 78, 0, 91, 131, 0, 142, 106, 158, + 145, 104, 103, 148, 151, 140, 88, 96, 46, 94, + 84, 143, 137, 138, 139, 49, 0, 82, 83, 80, + 81, 117, 118, 152, 153, 154, 132, 77, 0, 0, + 135, 120, 45, 0, 50, 56, 53, 48, 55, 147, + 134, 108, 149, 86, 100, 157, 101, 102, 128, 74, + 116, 52, 98, 0, 89, 70, 95, 71, 87, 110, + 47, 113, 85, 136, 119, 155, 0, 123, 0, 54, + 51, 0, 0, 112, 141, 114, 133, 107, 129, 79, + 122, 150, 99, 126, 0, 0, 0, 43, 0, 0, + 0, 0, 0, 0, 0, 0, 125, 146, 97, 127, + 69, 124, 0, 72, 75, 156, 144, 92, 93, 0, + 0, 0, 0, 0, 0, 0, 111, 115, 130, 105, + 0, 0, 0, 0, 0, 0, 0, 0, 90, 0, + 121, 0, 0, 0, 76, 73, 109, 0, 0, 0, + 78, 0, 91, 131, 0, 142, 106, 158, 145, 104, + 103, 148, 151, 140, 88, 96, 46, 94, 84, 143, + 137, 138, 139, 49, 0, 82, 83, 80, 81, 117, + 118, 152, 153, 154, 132, 77, 0, 0, 135, 120, + 45, 0, 50, 56, 53, 48, 55, 147, 134, 108, + 149, 86, 100, 157, 101, 102, 128, 74, 116, 52, + 98, 0, 89, 70, 95, 71, 87, 110, 47, 113, + 85, 136, 119, 155, 0, 123, 0, 54, 51, 0, + 0, 112, 141, 114, 133, 107, 129, 79, 122, 150, + 99, 126, 0, 0, 0, 180, 0, 0, 0, 0, + 0, 0, 0, 0, 125, 146, 97, 127, 69, 124, + 0, 72, 75, 156, 144, 92, 93, 0, 0, 0, + 0, 0, 0, 0, 111, 115, 130, 105, 0, 0, + 0, 0, 0, 0, 0, 0, 90, 0, 121, 0, + 0, 0, 76, 73, 109, 0, 0, 0, 78, 0, + 91, 131, 0, 142, 106, 158, 145, 104, 103, 148, + 151, 140, 88, 96, 46, 94, 84, 143, 137, 138, + 139, 49, 0, 82, 83, 80, 81, 117, 118, 152, + 153, 154, 132, 77, 0, 0, 135, 120, 45, 0, + 50, 56, 53, 48, 55, 147, 134, 108, 149, 86, + 100, 157, 101, 102, 128, 74, 116, 52, 98, 0, + 89, 70, 95, 71, 87, 110, 47, 113, 85, 136, + 119, 155, 0, 123, 0, 54, 51, 0, 0, 112, + 141, 114, 133, 107, 129, 79, 122, 150, 99, 126, + 0, 0, 0, 172, 0, 0, 0, 0, 0, 0, + 0, 0, 125, 146, 97, 127, 69, 124, 0, 72, + 75, 156, 144, 92, 93, 0, 0, 0, 0, 0, + 0, 0, 111, 115, 130, 105, 0, 0, 0, 0, + 0, 0, 0, 0, 90, 0, 121, 0, 0, 0, + 76, 73, 109, 0, 0, 0, 78, 0, 91, 131, + 0, 142, 106, 158, 145, 104, 103, 148, 151, 140, + 88, 96, 46, 94, 84, 143, 137, 138, 139, 49, + 0, 82, 83, 80, 81, 117, 118, 152, 153, 154, + 132, 77, 0, 0, 135, 120, 45, 0, 50, 56, + 53, 48, 55, 147, 134, 108, 149, 86, 100, 157, + 101, 102, 128, 74, 116, 52, 98, 0, 89, 70, + 95, 71, 87, 110, 47, 113, 85, 136, 119, 155, + 0, 123, 0, 54, 51, 0, 0, 112, 141, 114, + 133, 107, 129, 79, 122, 150, 99, 126, 0, 0, + 0, 66, 0, 0, 0, 0, 0, 0, 0, 0, + 125, 146, 97, 127, 69, 124, 0, 72, 75, 156, + 144, 92, 93, 0, 0, 0, 0, 0, 0, 0, + 111, 115, 130, 105, 0, 0, 0, 0, 0, 0, + 0, 0, 90, 0, 121, 0, 0, 0, 76, 73, + 109, 0, 0, 0, 78, 0, 91, 131, 0, 142, + 106, 158, 145, 104, 103, 148, 151, 140, 88, 96, + 46, 94, 84, 143, 137, 138, 139, 49, 0, 82, + 83, 80, 81, 117, 118, 152, 153, 154, 132, 77, + 0, 0, 135, 120, 45, 0, 50, 56, 53, 48, + 55, 52, 0, 0, 487, 0, 256, 0, 0, 0, + 47, 0, 0, 0, 0, 291, 0, 0, 0, 54, + 51, 0, 0, 0, 0, 284, 285, 0, 0, 0, + 0, 0, 0, 0, 219, 547, 0, 180, 274, 273, + 275, 276, 277, 278, 0, 0, 279, 271, 272, 0, + 0, 253, 265, 0, 290, 356, 355, 365, 366, 358, + 359, 360, 361, 362, 363, 364, 357, 0, 0, 367, + 0, 0, 0, 0, 262, 263, 324, 0, 0, 0, + 302, 0, 264, 0, 0, 261, 266, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 158, 0, 0, + 300, 0, 0, 0, 0, 0, 46, 0, 0, 0, + 0, 0, 0, 49, 0, 292, 301, 298, 299, 296, + 297, 295, 294, 293, 303, 286, 287, 289, 15, 288, + 45, 0, 50, 56, 53, 48, 55, 0, 0, 52, + 0, 0, 0, 0, 256, 0, 0, 0, 47, 0, + 0, 0, 0, 291, 0, 0, 0, 54, 51, 0, + 0, 0, 0, 284, 285, 0, 0, 0, 0, 0, + 0, 0, 219, 0, 0, 180, 274, 273, 275, 276, + 277, 278, 0, 0, 279, 271, 272, 0, 0, 253, + 265, 0, 290, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 126, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 64, 64, 64, 64, - 0, 0, 0, 0, 0, 0, 0, 64, 0, 0, - 126, 0, 0, 126, 0, 126, 200, 191, 165, 202, - 143, 157, 210, 158, 159, 185, 132, 173, 72, 155, - 0, 146, 128, 152, 129, 144, 167, 67, 170, 142, - 193, 176, 208, 0, 180, 0, 74, 71, 0, 0, - 169, 195, 171, 190, 164, 186, 137, 179, 203, 156, - 183, 0, 0, 0, 125, 0, 0, 0, 0, 0, - 0, 0, 0, 182, 199, 154, 184, 127, 181, 0, - 130, 133, 209, 197, 149, 150, 0, 0, 0, 0, - 0, 0, 0, 168, 172, 187, 162, 0, 0, 0, - 0, 0, 0, 622, 0, 147, 0, 178, 0, 0, - 0, 134, 131, 166, 0, 0, 0, 136, 0, 148, - 188, 0, 196, 163, 76, 198, 161, 160, 201, 204, - 194, 145, 153, 66, 151, 69, 0, 140, 141, 138, - 139, 174, 175, 205, 206, 207, 189, 135, 0, 0, - 192, 177, 65, 0, 70, 77, 73, 68, 75, 200, - 191, 165, 202, 143, 157, 210, 158, 159, 185, 132, - 173, 72, 155, 0, 146, 128, 152, 129, 144, 167, - 67, 170, 142, 193, 176, 208, 0, 180, 0, 74, - 71, 0, 0, 169, 195, 171, 190, 164, 186, 137, - 179, 203, 156, 183, 122, 0, 0, 125, 0, 0, - 0, 0, 0, 0, 0, 0, 182, 199, 154, 184, - 127, 181, 0, 130, 133, 209, 197, 149, 150, 0, - 0, 0, 0, 0, 0, 0, 168, 172, 187, 162, - 0, 0, 0, 0, 0, 0, 0, 0, 147, 0, - 178, 0, 0, 0, 134, 131, 166, 0, 0, 0, - 136, 0, 148, 188, 0, 196, 163, 76, 198, 161, - 160, 201, 204, 194, 145, 153, 66, 151, 69, 0, - 140, 141, 138, 139, 174, 175, 205, 206, 207, 189, - 135, 0, 0, 192, 177, 65, 0, 70, 77, 73, - 68, 75, 200, 191, 165, 202, 143, 157, 210, 158, - 159, 185, 132, 173, 72, 155, 0, 146, 128, 152, - 129, 144, 167, 67, 170, 142, 193, 176, 208, 0, - 180, 0, 74, 71, 0, 0, 169, 195, 171, 190, - 164, 186, 137, 179, 203, 156, 183, 0, 0, 0, - 84, 0, 0, 0, 0, 0, 0, 0, 0, 182, - 199, 154, 184, 127, 181, 0, 130, 133, 209, 197, - 149, 150, 0, 0, 0, 0, 0, 0, 0, 168, - 172, 187, 162, 0, 0, 0, 0, 0, 0, 511, - 0, 147, 0, 178, 0, 0, 0, 134, 131, 166, - 0, 0, 0, 136, 0, 148, 188, 0, 196, 163, - 76, 198, 161, 160, 201, 204, 194, 145, 153, 66, - 151, 69, 0, 140, 141, 138, 139, 174, 175, 205, - 206, 207, 189, 135, 0, 0, 192, 177, 65, 0, - 70, 77, 73, 68, 75, 200, 191, 165, 202, 143, - 157, 210, 158, 159, 185, 132, 173, 72, 155, 0, - 146, 128, 152, 129, 144, 167, 67, 170, 142, 193, - 176, 208, 0, 180, 0, 74, 71, 0, 0, 169, - 195, 171, 190, 164, 186, 137, 179, 203, 156, 183, - 0, 0, 0, 125, 0, 0, 0, 0, 0, 0, - 0, 0, 182, 199, 154, 184, 127, 181, 0, 130, - 133, 209, 197, 149, 150, 0, 0, 0, 0, 0, - 0, 0, 168, 172, 187, 162, 0, 0, 0, 0, - 0, 0, 0, 0, 147, 0, 178, 0, 0, 0, - 134, 131, 166, 0, 0, 0, 136, 0, 148, 188, - 0, 196, 163, 76, 198, 161, 160, 201, 204, 194, - 145, 153, 66, 151, 69, 0, 140, 141, 138, 139, - 174, 175, 205, 206, 207, 189, 135, 0, 0, 192, - 177, 65, 0, 70, 77, 73, 68, 75, 200, 191, - 165, 202, 143, 157, 210, 158, 159, 185, 132, 173, - 72, 155, 0, 146, 128, 152, 129, 144, 167, 67, - 170, 142, 193, 176, 208, 0, 180, 0, 74, 71, - 0, 0, 169, 195, 171, 190, 164, 186, 137, 179, - 203, 156, 183, 0, 0, 0, 84, 0, 0, 0, - 0, 0, 0, 0, 0, 182, 199, 154, 184, 127, - 181, 0, 130, 133, 209, 197, 149, 150, 0, 0, - 0, 0, 0, 0, 0, 168, 172, 187, 162, 0, - 0, 0, 0, 0, 0, 0, 0, 147, 0, 178, - 0, 0, 0, 134, 131, 166, 0, 0, 0, 136, - 0, 148, 188, 0, 196, 163, 76, 198, 161, 160, - 201, 204, 194, 145, 153, 66, 151, 69, 0, 140, - 141, 138, 139, 174, 175, 205, 206, 207, 189, 135, - 0, 0, 192, 177, 65, 0, 70, 77, 73, 68, - 75, 200, 191, 165, 202, 143, 157, 210, 158, 159, - 185, 132, 173, 72, 155, 0, 146, 128, 152, 129, - 144, 167, 67, 170, 142, 193, 176, 208, 0, 180, - 0, 74, 71, 0, 0, 169, 195, 171, 190, 164, - 186, 137, 179, 203, 156, 183, 0, 0, 0, 63, - 0, 0, 0, 0, 0, 0, 0, 0, 182, 199, - 154, 184, 127, 181, 0, 130, 133, 209, 197, 149, - 150, 0, 0, 0, 0, 0, 0, 0, 168, 172, - 187, 162, 0, 0, 0, 0, 0, 0, 0, 0, - 147, 0, 178, 0, 0, 0, 134, 131, 166, 0, - 0, 0, 136, 0, 148, 188, 0, 196, 163, 76, - 198, 161, 160, 201, 204, 194, 145, 153, 66, 151, - 69, 0, 140, 141, 138, 139, 174, 175, 205, 206, - 207, 189, 135, 0, 0, 192, 177, 65, 0, 70, - 77, 73, 68, 75, 72, 0, 0, 0, 0, 245, - 0, 0, 0, 67, 0, 0, 0, 0, 280, 0, - 0, 0, 74, 71, 0, 0, 0, 0, 273, 274, - 0, 0, 0, 0, 0, 0, 0, 122, 0, 0, - 84, 263, 262, 264, 265, 266, 267, 0, 0, 268, - 260, 261, 0, 0, 242, 254, 0, 279, 0, 0, + 0, 0, 262, 263, 0, 0, 0, 0, 302, 0, + 264, 0, 0, 261, 266, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 158, 0, 0, 300, 0, + 0, 0, 0, 0, 46, 0, 0, 0, 0, 0, + 0, 49, 0, 292, 301, 298, 299, 296, 297, 295, + 294, 293, 303, 286, 287, 289, 0, 288, 45, 0, + 50, 56, 53, 48, 55, 52, 0, 0, 0, 0, + 256, 0, 0, 0, 47, 0, 0, 0, 0, 291, + 0, 0, 0, 54, 51, 0, 0, 0, 0, 284, + 285, 0, 0, 0, 0, 0, 0, 0, 219, 0, + 0, 180, 274, 273, 275, 276, 277, 278, 0, 0, + 279, 271, 272, 0, 0, 253, 265, 0, 290, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 251, 252, 313, - 0, 0, 0, 291, 0, 253, 0, 0, 250, 255, + 0, 0, 0, 0, 0, 0, 0, 0, 262, 263, + 0, 0, 0, 0, 302, 0, 264, 0, 0, 261, + 266, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 158, 0, 0, 300, 0, 0, 0, 0, 0, + 46, 0, 0, 0, 0, 0, 0, 49, 0, 292, + 301, 298, 299, 296, 297, 295, 294, 293, 303, 286, + 287, 289, 52, 288, 45, 0, 50, 56, 53, 48, + 55, 47, 0, 0, 0, 0, 291, 0, 0, 0, + 54, 51, 0, 0, 0, 0, 284, 285, 0, 0, + 0, 0, 0, 0, 0, 219, 0, 0, 180, 274, + 273, 275, 276, 277, 278, 0, 0, 279, 271, 272, + 0, 0, 0, 265, 0, 290, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 76, 0, 0, 289, 0, 0, 0, 0, 0, 66, - 0, 69, 0, 281, 290, 287, 288, 285, 286, 284, - 283, 282, 292, 275, 276, 278, 0, 277, 65, 0, - 70, 77, 73, 68, 75, 72, 0, 0, 0, 0, - 245, 0, 0, 0, 67, 0, 0, 0, 0, 280, - 0, 0, 0, 74, 71, 0, 0, 0, 0, 273, - 274, 0, 0, 0, 0, 0, 0, 0, 122, 0, - 426, 84, 263, 262, 264, 265, 266, 267, 0, 0, - 268, 260, 261, 0, 0, 242, 254, 0, 279, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 251, 252, - 0, 0, 0, 0, 291, 0, 253, 0, 0, 250, - 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 76, 0, 0, 289, 0, 0, 0, 0, 0, - 66, 0, 69, 0, 281, 290, 287, 288, 285, 286, - 284, 283, 282, 292, 275, 276, 278, 14, 277, 65, - 0, 70, 77, 73, 68, 75, 0, 0, 72, 0, - 0, 0, 0, 245, 0, 0, 0, 67, 0, 0, - 0, 0, 280, 0, 0, 0, 74, 71, 0, 0, - 0, 0, 273, 274, 0, 0, 0, 0, 0, 0, - 0, 122, 0, 0, 84, 263, 262, 264, 265, 266, - 267, 0, 0, 268, 260, 261, 0, 0, 242, 254, - 0, 279, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 251, 252, 0, 0, 0, 0, 291, 0, 253, - 0, 0, 250, 255, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 76, 0, 0, 289, 0, 0, - 0, 0, 0, 66, 0, 69, 0, 281, 290, 287, - 288, 285, 286, 284, 283, 282, 292, 275, 276, 278, - 0, 277, 65, 0, 70, 77, 73, 68, 75, 72, - 0, 0, 0, 0, 245, 0, 0, 0, 67, 0, - 0, 0, 0, 280, 0, 0, 0, 74, 71, 0, - 0, 0, 0, 273, 274, 0, 0, 0, 0, 0, - 0, 0, 122, 0, 0, 84, 263, 262, 264, 265, - 266, 267, 0, 0, 268, 260, 261, 0, 0, 242, - 254, 0, 279, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 251, 252, 0, 0, 0, 0, 291, 0, - 253, 0, 0, 250, 255, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 76, 0, 0, 289, 0, - 0, 0, 0, 0, 66, 0, 69, 0, 281, 290, - 287, 288, 285, 286, 284, 283, 282, 292, 275, 276, - 278, 72, 277, 65, 0, 70, 77, 73, 68, 75, - 67, 0, 0, 0, 0, 280, 0, 0, 0, 74, - 71, 0, 0, 0, 0, 273, 274, 0, 0, 0, - 0, 0, 0, 0, 122, 0, 0, 84, 263, 262, - 264, 265, 266, 267, 0, 0, 268, 260, 261, 0, - 0, 0, 254, 0, 279, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 251, 252, 0, 0, 0, 0, - 291, 0, 253, 0, 0, 250, 255, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 76, 0, 0, - 289, 0, 0, 0, 0, 0, 66, 0, 69, 0, - 281, 290, 287, 288, 285, 286, 284, 283, 282, 292, - 275, 276, 278, 72, 277, 65, 0, 70, 77, 73, - 68, 75, 67, 0, 0, 0, 0, 280, 0, 0, - 0, 74, 71, 0, 0, 0, 0, 273, 274, 0, - 0, 0, 0, 0, 0, 0, 122, 0, 0, 84, - 263, 262, 264, 265, 266, 267, 0, 0, 268, 0, - 72, 0, 0, 0, 254, 0, 279, 0, 0, 67, - 0, 0, 72, 0, 0, 0, 0, 0, 74, 71, - 0, 67, 0, 0, 0, 0, 251, 252, 0, 0, - 74, 71, 291, 122, 253, 0, 63, 250, 255, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 125, 76, - 414, 0, 289, 0, 0, 0, 0, 0, 66, 0, - 69, 0, 281, 290, 287, 288, 285, 286, 284, 283, - 282, 292, 275, 276, 278, 72, 277, 65, 0, 70, - 77, 73, 68, 75, 67, 0, 0, 0, 0, 0, - 0, 0, 0, 74, 71, 0, 76, 0, 0, 0, - 0, 0, 0, 0, 0, 66, 0, 69, 76, 0, - 0, 63, 0, 110, 72, 0, 0, 66, 0, 69, - 0, 0, 103, 67, 65, 0, 70, 77, 73, 68, - 75, 0, 74, 71, 0, 0, 65, 0, 70, 77, - 73, 68, 75, 72, 0, 0, 0, 0, 0, 0, - 63, 72, 67, 0, 0, 0, 0, 0, 72, 0, - 67, 74, 71, 0, 0, 0, 0, 67, 0, 74, - 71, 76, 0, 0, 0, 0, 74, 71, 0, 84, - 66, 0, 69, 0, 0, 0, 0, 125, 0, 0, - 0, 0, 0, 0, 63, 0, 0, 0, 0, 65, - 0, 70, 77, 73, 68, 75, 0, 0, 0, 0, - 76, 0, 0, 0, 0, 0, 0, 0, 0, 66, - 0, 69, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 65, 76, - 70, 77, 73, 68, 75, 0, 0, 76, 66, 0, - 69, 0, 0, 0, 76, 0, 66, 0, 69, 0, - 0, 0, 0, 66, 0, 69, 0, 65, 0, 70, - 77, 73, 68, 75, 0, 65, 0, 70, 77, 73, - 68, 75, 65, 0, 70, 77, 73, 68, 75, 340, - 0, 343, 0, 0, 0, 0, 0, 357, 358, 359, - 360, 361, 362, 363, 0, 341, 342, 339, 345, 344, - 354, 355, 347, 348, 349, 350, 351, 352, 353, 346, - 0, 0, 356, + 0, 0, 0, 0, 0, 262, 263, 0, 0, 0, + 0, 302, 0, 264, 0, 0, 261, 266, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 158, 0, + 0, 300, 0, 0, 0, 0, 0, 46, 0, 0, + 0, 0, 0, 0, 49, 0, 292, 301, 298, 299, + 296, 297, 295, 294, 293, 303, 286, 287, 289, 52, + 288, 45, 0, 50, 56, 53, 48, 55, 47, 0, + 0, 0, 0, 291, 0, 0, 0, 54, 51, 0, + 0, 0, 0, 284, 285, 0, 0, 0, 0, 0, + 0, 0, 219, 0, 0, 180, 274, 273, 275, 276, + 277, 278, 0, 0, 279, 0, 0, 0, 0, 0, + 265, 0, 290, 0, 0, 0, 52, 0, 0, 0, + 205, 0, 0, 0, 0, 47, 0, 0, 0, 0, + 52, 0, 262, 263, 54, 51, 0, 0, 302, 47, + 264, 0, 0, 261, 266, 0, 0, 0, 54, 51, + 0, 0, 172, 0, 207, 158, 0, 0, 300, 0, + 0, 0, 0, 0, 46, 0, 43, 0, 0, 0, + 0, 49, 0, 292, 301, 298, 299, 296, 297, 295, + 294, 293, 303, 286, 287, 289, 0, 288, 45, 0, + 50, 56, 53, 48, 55, 52, 0, 0, 0, 423, + 0, 0, 0, 0, 47, 0, 0, 0, 0, 0, + 0, 0, 158, 54, 51, 0, 0, 0, 0, 0, + 52, 46, 0, 0, 38, 0, 40, 0, 49, 47, + 41, 43, 0, 425, 0, 46, 0, 0, 54, 51, + 0, 0, 49, 346, 345, 45, 0, 50, 56, 53, + 48, 55, 0, 219, 0, 0, 172, 0, 0, 45, + 347, 50, 56, 53, 48, 55, 52, 0, 0, 0, + 0, 0, 0, 0, 52, 47, 0, 0, 0, 0, + 0, 0, 0, 47, 54, 51, 0, 0, 0, 0, + 0, 158, 54, 51, 0, 0, 0, 0, 0, 0, + 46, 0, 43, 0, 425, 0, 0, 49, 0, 0, + 172, 0, 207, 0, 0, 0, 158, 0, 0, 0, + 0, 0, 0, 0, 45, 46, 50, 56, 53, 48, + 55, 0, 49, 0, 0, 0, 0, 0, 52, 0, + 0, 0, 0, 0, 0, 0, 200, 47, 0, 45, + 0, 50, 56, 53, 48, 55, 54, 51, 0, 0, + 0, 0, 158, 0, 0, 0, 0, 0, 0, 0, + 158, 46, 0, 0, 172, 0, 0, 0, 49, 46, + 0, 0, 0, 0, 0, 0, 49, 0, 0, 0, + 0, 0, 0, 52, 0, 45, 0, 50, 56, 53, + 48, 55, 47, 45, 52, 50, 56, 53, 48, 55, + 0, 54, 51, 47, 0, 0, 0, 0, 52, 0, + 0, 0, 54, 51, 0, 0, 0, 47, 0, 180, + 0, 0, 0, 0, 158, 0, 54, 51, 0, 0, + 43, 0, 0, 46, 0, 0, 0, 0, 0, 0, + 49, 0, 0, 0, 172, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 45, 0, 50, + 56, 53, 48, 55, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 158, + 0, 0, 0, 0, 0, 0, 0, 0, 46, 0, + 158, 0, 0, 0, 0, 49, 0, 0, 0, 46, + 0, 0, 0, 0, 158, 0, 49, 0, 0, 0, + 0, 0, 45, 46, 50, 56, 53, 48, 55, 0, + 49, 0, 0, 45, 0, 50, 56, 53, 48, 55, + 0, 0, 0, 0, 0, 0, 351, 45, 354, 50, + 56, 53, 48, 55, 368, 369, 370, 371, 372, 373, + 374, 0, 352, 353, 350, 356, 355, 365, 366, 358, + 359, 360, 361, 362, 363, 364, 357, 0, 0, 367, } var yyPact = [...]int{ - 706, -1000, -115, 532, -1000, -1000, -1000, -1000, -1000, -1000, - -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -47, - -40, -14, 25, -17, -1000, -1000, -1000, -1000, -1000, 528, - 469, 392, -1000, -35, 2951, 515, 2936, -45, -22, 2951, - -1000, -20, 2951, -1000, 2951, -48, 238, -48, 2951, -1000, - -1000, -1000, -1000, -1000, -1000, 431, -1000, -1000, 156, 2907, - 448, 1027, 42, -1000, -1000, -1000, -1000, -1000, -1000, -1000, - -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, 2951, 243, - -1000, 104, -1000, 41, -1000, -1000, 2951, 122, 195, 1404, - 2951, 1404, -31, 2951, 459, 344, 2951, -1000, 349, 1066, - -1000, -1000, 367, 2951, -1000, 2936, 93, -1000, 2868, -1000, - -1000, 1976, 508, 2936, 2502, 1833, 1404, 457, -55, -1000, + 1182, -1000, -120, 484, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, + 2933, -1, 16, 4, 15, 2198, -1000, -1000, -1000, -1000, + 479, 431, 326, -1000, -31, 3221, 467, 3196, -48, 11, + 3221, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, + -1000, -1000, -1000, -1000, -1000, -1000, -1000, 0, 3221, -1000, + 3221, -58, 159, -58, 3221, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, @@ -716,147 +746,156 @@ var yyPact = [...]int{ -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, - -1000, 23, -1000, 2951, -1000, -1000, 2951, 1404, 2107, -1000, - 481, -1000, 432, 427, 449, 2936, 2944, -1000, 256, -1000, - -24, -25, -27, -1000, -1000, -1000, -1000, 500, 2502, -1000, - 108, -1000, 2502, 3032, -1000, 277, -1000, -4, -1000, -1000, - 2746, 2746, 2746, 2746, 2746, 2746, -1000, -1000, -1000, -1000, - -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, 277, - 32, -1000, 2371, 277, 277, 277, 277, 277, 277, 2502, - 277, 277, 277, 277, 277, 277, 277, 277, 277, 277, - 277, 277, 277, -1000, 26, -1000, -1000, -1000, 338, 2944, - -1000, -33, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, - -1000, 343, -1000, -1000, 985, 10, 2951, -1000, -1000, -1000, - -1000, 334, 277, 532, 266, 340, 7, 500, 277, 277, - 277, 476, 498, 108, 2502, 2502, 197, 72, 2624, 161, - 103, 2746, 2746, 2746, 2746, 2746, 2746, 2746, 2746, 2746, - 2746, 2746, 2746, 2746, 2746, 2746, 5, -1000, -1000, -1000, - -1000, -1000, -1000, -1000, -1000, 528, 447, 447, 28, 28, - 28, 28, 28, 69, 814, 1833, 293, 300, 108, 2107, - 2107, 2502, 2502, 2944, 470, 106, 108, 2944, -1000, 200, - -1000, -1000, -1000, -1000, -1000, -1000, -1000, 2107, 2107, 2107, - 2107, 1690, 2951, -1000, -1000, 2951, -1000, 508, 2107, 2793, - -1000, -1000, 2805, -1000, -1000, 1547, -1000, -1000, 441, 289, - -1000, -1000, 2238, -1000, -1000, 2944, -1000, 2944, 476, 2944, - 2944, 2944, -1000, 2502, 2502, 72, 110, -1000, -1000, 166, - -1000, -1000, -1000, 876, -1000, -1000, -1000, -1000, 161, 2746, - 2746, 2746, 193, 876, 799, 145, 898, 28, 209, 209, - 50, 50, 50, 50, 50, 829, 829, -1000, -1000, -1000, - -1000, -1000, -1000, -1000, -1000, 200, 2107, 272, 277, -1000, - 2502, -1000, 274, 274, 102, 276, 298, -1000, 2107, 112, - -1000, 2502, 200, -1000, 274, 200, 274, 274, -1000, 1404, - -1000, 505, -1000, 270, 697, -1000, -1000, -1000, 465, 1011, - -1000, -1000, 0, 451, 277, -1000, -5, -1000, -1000, 286, - -1000, 286, 286, 278, 268, -1000, 246, -1000, -1000, -1000, - -1000, 193, 876, 664, -1000, 2746, 2746, -1000, 274, 2107, - 108, -1000, -1000, 5, 5, 5, -85, 2944, 282, 107, - -1000, 2502, 248, -1000, -1000, -1000, -1000, -1000, -1000, 503, - 492, 2793, 2793, 2793, 2793, -1000, 374, 368, -1000, 389, - 381, 407, 2951, -1000, 214, 1261, 524, -1000, 2944, -1000, - 2944, -1000, -1000, 2502, 2502, 2502, -1000, -1000, -1000, -1000, - 2746, 876, 876, -1000, 200, 200, 125, 200, 200, 277, - -1000, -82, -1000, 108, 2502, 500, 2502, 2502, 697, 330, - 984, -1000, -1000, -1000, -1000, 359, -1000, 358, -1000, -1000, - -1000, -1000, -1000, 2936, -1000, -1000, 108, 108, -1000, 876, - -1000, -1000, -1000, 169, -1000, 444, -1000, -1000, 2746, 200, - 199, 108, 476, 108, 249, 2502, 2502, -1000, -1000, 243, - 205, 5, 109, -1000, -1000, 438, 108, 108, 24, 167, - -1000, 200, 9, -99, -1000, 521, 86, -1000, 440, 200, - -1000, 390, -94, -105, -1000, 403, 5, -1000, -1000, 386, - -1000, 383, -1000, -97, -1000, -101, -112, -1000, + -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, + -1000, -1000, -1000, -1000, 375, -1000, -1000, 123, 3141, 389, + 2919, 54, -1000, -1000, 3221, 171, -1000, 44, -1000, 53, + -1000, -1000, 3221, 108, 152, 1458, 3221, 1458, -25, 3221, + 421, 266, 3221, -1000, -1000, 284, 721, -1000, -1000, 287, + 3221, -1000, 3196, 13, -1000, 3077, -1000, -1000, 2050, 460, + 3196, 2608, 1902, 1458, 411, -62, -1000, -1000, -1000, -1000, + -1000, -1000, 35, -1000, 3221, -1000, -1000, 3221, 1458, 927, + -1000, 448, -1000, 372, 369, 405, 3196, 3207, -1000, 285, + -1000, 9, 8, 6, -1000, -1000, -1000, -1000, 453, 2608, + -1000, 199, -1000, 2608, 3299, -1000, 187, -1000, 25, -1000, + -1000, 2862, 2862, 2862, 2862, 2862, 2862, -1000, -1000, -1000, + -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, + 187, 50, -1000, 2472, 187, 187, 187, 187, 187, 187, + 2608, 187, 187, 187, 187, 187, 187, 187, 187, 187, + 187, 187, 187, 187, -1000, 34, -1000, -1000, -1000, 263, + 3207, -1000, -29, -1000, -1000, -1000, -1000, -1000, -1000, -1000, + -1000, -1000, 209, -1000, -1000, 2998, 33, 3221, -1000, -1000, + -1000, -1000, 260, 187, 484, 189, 253, 29, 453, 187, + 187, 187, 443, 446, 199, 2608, 2608, 165, 76, 2735, + 131, 94, 2862, 2862, 2862, 2862, 2862, 2862, 2862, 2862, + 2862, 2862, 2862, 2862, 2862, 2862, 2862, 12, -1000, -1000, + -1000, -1000, -1000, -1000, -1000, -1000, 479, 264, 264, 40, + 40, 40, 40, 40, 789, 2334, 1902, 261, 249, 199, + 927, 927, 2608, 2608, 3207, 437, 93, 199, 3207, -1000, + 163, -1000, -1000, -1000, -1000, -1000, -1000, -1000, 927, 927, + 927, 927, 1754, 3221, -1000, -1000, 3221, -1000, 460, 927, + 3023, -1000, -1000, 3069, -1000, -1000, 1606, -1000, -1000, 401, + 255, -1000, -1000, 1116, -1000, -1000, 3207, -1000, 3207, 443, + 3207, 3207, 3207, -1000, 2608, 2608, 76, 104, -1000, -1000, + 214, -1000, -1000, -1000, 768, -1000, -1000, -1000, -1000, 131, + 2862, 2862, 2862, 10, 768, 2319, 993, 1183, 40, 107, + 107, 72, 72, 72, 72, 72, 55, 55, -1000, -1000, + -1000, -1000, -1000, -1000, -1000, -1000, 163, 927, 247, 187, + -1000, 2608, -1000, 204, 204, 43, 226, 243, -1000, 927, + 100, -1000, 2608, 163, -1000, 204, 163, 204, 204, -1000, + 1458, -1000, 457, -1000, 219, 478, -1000, -1000, -1000, 423, + 1074, -1000, -1000, 28, 377, 187, -1000, 19, -1000, -1000, + 238, -1000, 238, 238, 206, 202, -1000, 197, -1000, -1000, + -1000, -1000, 10, 768, 702, -1000, 2862, 2862, -1000, 204, + 927, 199, -1000, -1000, 12, 12, 12, -92, 3207, 288, + 88, -1000, 2608, 186, -1000, -1000, -1000, -1000, -1000, -1000, + 455, 445, 3023, 3023, 3023, 3023, -1000, 323, 322, -1000, + 304, 301, 305, 3221, -1000, 174, 1310, 472, -1000, 3207, + -1000, 3207, -1000, -1000, 2608, 2608, 2608, -1000, -1000, -1000, + -1000, 2862, 768, 768, -1000, 163, 163, 37, 163, 163, + 187, -1000, -89, -1000, 199, 2608, 453, 2608, 2608, 478, + 201, 289, -1000, -1000, -1000, -1000, 321, -1000, 312, -1000, + -1000, -1000, -1000, -1000, 3196, -1000, -1000, 199, 199, -1000, + 768, -1000, -1000, -1000, 137, -1000, 381, -1000, -1000, 2862, + 163, 139, 199, 443, 199, 183, 2608, 2608, -1000, -1000, + 171, 145, 12, -14, -1000, -1000, 391, 199, 199, 38, + 134, -1000, 163, 7, -100, -1000, 471, 56, -1000, 376, + 163, -1000, 357, -96, -104, -1000, 359, 12, -1000, -1000, + 332, -1000, 291, -1000, -98, -1000, -102, -106, -1000, } var yyPgo = [...]int{ - 0, 661, 52, 660, 658, 657, 655, 654, 653, 652, - 647, 643, 642, 524, 641, 639, 31, 638, 637, 635, - 633, 12, 59, 10, 28, 19, 632, 18, 24, 9, - 628, 623, 13, 622, 29, 621, 450, 619, 30, 39, - 618, 43, 617, 616, 32, 20, 614, 613, 612, 610, - 608, 607, 25, 7, 600, 16, 15, 593, 17, 1, - 591, 22, 590, 589, 587, 583, 3, 577, 11, 576, - 2, 573, 566, 565, 564, 23, 6, 93, 563, 441, - 347, 562, 561, 558, 555, 554, 36, 684, 218, 8, - 21, 553, 5, 27, 142, 551, 549, 35, 26, 4, - 14, 547, 546, 538, 418, 0, 388, + 0, 629, 38, 628, 627, 626, 625, 624, 623, 618, + 613, 611, 608, 604, 510, 603, 602, 34, 601, 600, + 596, 587, 15, 37, 10, 22, 19, 584, 23, 24, + 14, 583, 577, 12, 576, 124, 575, 399, 574, 32, + 21, 573, 40, 572, 571, 28, 20, 569, 564, 562, + 560, 559, 557, 26, 2, 555, 16, 17, 554, 18, + 6, 553, 13, 552, 551, 549, 548, 3, 547, 8, + 546, 1, 545, 540, 535, 532, 35, 7, 30, 516, + 402, 182, 515, 513, 512, 503, 502, 36, 722, 259, + 11, 31, 501, 5, 27, 42, 500, 499, 29, 25, + 4, 9, 492, 491, 490, 484, 431, 0, 59, } var yyR1 = [...]int{ - 0, 101, 102, 102, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 2, 2, 2, 3, 3, - 4, 5, 6, 7, 7, 7, 8, 8, 8, 9, - 10, 10, 10, 11, 12, 12, 12, 103, 13, 14, - 14, 15, 15, 15, 18, 18, 18, 16, 16, 17, - 17, 23, 23, 22, 22, 24, 24, 24, 24, 91, - 91, 91, 90, 90, 26, 26, 27, 27, 28, 28, - 29, 29, 29, 36, 30, 30, 30, 30, 96, 96, - 95, 95, 95, 94, 94, 31, 31, 31, 31, 32, - 32, 32, 32, 33, 33, 35, 35, 34, 34, 37, - 37, 37, 37, 38, 38, 39, 39, 25, 25, 25, - 25, 25, 25, 41, 41, 40, 40, 40, 40, 40, - 40, 40, 40, 40, 40, 40, 40, 51, 51, 51, - 51, 51, 51, 42, 42, 42, 42, 42, 42, 42, - 21, 21, 52, 52, 52, 58, 53, 53, 99, 99, - 99, 99, 45, 45, 45, 45, 45, 45, 45, 45, - 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, - 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, - 45, 49, 49, 49, 47, 47, 47, 47, 47, 47, - 47, 47, 47, 48, 48, 48, 48, 48, 48, 48, - 48, 106, 106, 50, 50, 50, 50, 19, 19, 19, - 19, 19, 100, 100, 100, 100, 100, 100, 100, 100, - 62, 62, 20, 20, 60, 60, 61, 63, 63, 59, - 59, 59, 44, 44, 44, 44, 44, 44, 44, 46, - 46, 46, 64, 64, 65, 65, 66, 66, 67, 67, - 68, 69, 69, 69, 70, 70, 70, 70, 71, 71, - 71, 72, 72, 74, 74, 73, 73, 73, 73, 75, - 75, 43, 43, 54, 54, 56, 56, 55, 57, 76, - 76, 77, 78, 78, 80, 80, 81, 81, 79, 79, - 82, 82, 82, 82, 82, 82, 83, 83, 84, 84, - 85, 85, 88, 88, 89, 89, 92, 92, 93, 93, - 86, 86, 86, 86, 86, 86, 86, 86, 86, 86, - 86, 86, 86, 86, 86, 86, 86, 86, 86, 86, - 86, 86, 86, 86, 86, 86, 86, 86, 86, 86, - 86, 86, 86, 86, 86, 86, 86, 86, 86, 86, - 86, 86, 86, 86, 86, 86, 86, 86, 86, 86, - 86, 86, 86, 86, 86, 86, 86, 86, 86, 86, - 86, 86, 86, 86, 86, 86, 86, 86, 86, 86, - 86, 86, 86, 86, 86, 86, 86, 86, 86, 86, - 86, 86, 86, 86, 87, 87, 87, 87, 87, 87, - 87, 87, 87, 87, 87, 87, 87, 104, 105, 97, - 98, 98, 98, + 0, 103, 104, 104, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 2, 2, 2, 3, + 3, 4, 5, 6, 7, 7, 7, 8, 8, 8, + 9, 10, 10, 10, 11, 102, 102, 102, 12, 13, + 13, 105, 14, 15, 15, 16, 16, 16, 19, 19, + 19, 17, 17, 18, 18, 24, 24, 23, 23, 25, + 25, 25, 25, 92, 92, 92, 91, 91, 27, 27, + 28, 28, 29, 29, 30, 30, 30, 37, 31, 31, + 31, 31, 97, 97, 96, 96, 96, 95, 95, 32, + 32, 32, 32, 33, 33, 33, 33, 34, 34, 36, + 36, 35, 35, 38, 38, 38, 38, 39, 39, 40, + 40, 26, 26, 26, 26, 26, 26, 42, 42, 41, + 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, + 41, 52, 52, 52, 52, 52, 52, 43, 43, 43, + 43, 43, 43, 43, 22, 22, 53, 53, 53, 59, + 54, 54, 100, 100, 100, 100, 46, 46, 46, 46, + 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, + 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, + 46, 46, 46, 46, 46, 50, 50, 50, 48, 48, + 48, 48, 48, 48, 48, 48, 48, 49, 49, 49, + 49, 49, 49, 49, 49, 108, 108, 51, 51, 51, + 51, 20, 20, 20, 20, 20, 101, 101, 101, 101, + 101, 101, 101, 101, 63, 63, 21, 21, 61, 61, + 62, 64, 64, 60, 60, 60, 45, 45, 45, 45, + 45, 45, 45, 47, 47, 47, 65, 65, 66, 66, + 67, 67, 68, 68, 69, 70, 70, 70, 71, 71, + 71, 71, 72, 72, 72, 73, 73, 75, 75, 74, + 74, 74, 74, 76, 76, 44, 44, 55, 55, 57, + 57, 56, 58, 77, 77, 78, 79, 79, 81, 81, + 82, 82, 80, 80, 83, 83, 83, 83, 83, 83, + 84, 84, 85, 85, 85, 86, 86, 89, 89, 90, + 90, 93, 93, 94, 94, 87, 87, 87, 87, 87, + 87, 87, 87, 87, 87, 87, 87, 87, 87, 87, + 87, 87, 87, 87, 87, 87, 87, 87, 87, 87, + 87, 87, 87, 87, 87, 87, 87, 87, 87, 87, + 87, 87, 87, 87, 87, 87, 87, 87, 87, 87, + 87, 87, 87, 87, 87, 87, 87, 87, 87, 87, + 87, 87, 87, 87, 87, 87, 87, 87, 87, 87, + 87, 87, 87, 87, 87, 87, 87, 87, 87, 87, + 87, 87, 87, 87, 87, 87, 87, 87, 87, 87, + 87, 87, 87, 87, 88, 88, 88, 88, 88, 88, + 88, 88, 88, 88, 88, 88, 88, 106, 107, 98, + 99, 99, 99, } var yyR2 = [...]int{ 0, 2, 0, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 13, 7, 3, 7, 7, - 8, 7, 3, 5, 8, 4, 6, 7, 4, 5, - 4, 5, 5, 3, 2, 2, 2, 0, 2, 0, - 2, 1, 2, 2, 0, 1, 1, 0, 1, 0, - 1, 0, 1, 1, 3, 1, 2, 3, 5, 0, - 1, 2, 1, 1, 0, 2, 1, 3, 1, 1, - 1, 3, 3, 3, 3, 5, 5, 3, 0, 1, - 0, 1, 2, 1, 1, 1, 2, 2, 1, 2, - 3, 2, 3, 2, 2, 2, 1, 1, 3, 0, - 5, 5, 5, 1, 3, 0, 2, 1, 3, 3, - 2, 3, 1, 1, 1, 1, 3, 3, 3, 4, - 4, 5, 3, 4, 5, 6, 2, 1, 2, 1, - 2, 1, 2, 1, 1, 1, 1, 1, 1, 1, - 0, 2, 1, 1, 1, 3, 1, 3, 1, 1, - 1, 1, 1, 1, 1, 1, 3, 3, 3, 3, + 1, 1, 1, 1, 1, 1, 13, 7, 3, 7, + 7, 8, 7, 3, 5, 8, 4, 6, 7, 4, + 5, 4, 5, 5, 3, 1, 1, 1, 3, 2, + 2, 0, 2, 0, 2, 1, 2, 2, 0, 1, + 1, 0, 1, 0, 1, 0, 1, 1, 3, 1, + 2, 3, 5, 0, 1, 2, 1, 1, 0, 2, + 1, 3, 1, 1, 1, 3, 3, 3, 3, 5, + 5, 3, 0, 1, 0, 1, 2, 1, 1, 1, + 2, 2, 1, 2, 3, 2, 3, 2, 2, 2, + 1, 1, 3, 0, 5, 5, 5, 1, 3, 0, + 2, 1, 3, 3, 2, 3, 1, 1, 1, 1, + 3, 3, 3, 4, 4, 5, 3, 4, 5, 6, + 2, 1, 2, 1, 2, 1, 2, 1, 1, 1, + 1, 1, 1, 1, 0, 2, 1, 1, 1, 3, + 1, 3, 1, 1, 1, 1, 1, 1, 1, 1, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, - 3, 2, 2, 2, 2, 2, 3, 1, 1, 1, - 1, 4, 5, 6, 4, 4, 6, 6, 6, 9, - 7, 5, 4, 2, 2, 2, 2, 2, 2, 2, - 2, 0, 2, 4, 4, 4, 4, 0, 3, 4, - 7, 3, 1, 2, 4, 5, 7, 2, 4, 6, - 0, 1, 0, 2, 1, 2, 4, 0, 2, 1, - 3, 5, 1, 1, 1, 1, 1, 1, 1, 1, - 2, 2, 0, 3, 0, 2, 0, 3, 1, 3, - 2, 0, 1, 1, 0, 2, 4, 4, 0, 2, - 4, 1, 3, 0, 3, 1, 3, 3, 5, 0, - 5, 2, 1, 1, 3, 1, 2, 3, 1, 1, - 3, 3, 1, 1, 0, 2, 0, 3, 0, 1, - 1, 1, 1, 1, 1, 1, 0, 1, 0, 1, - 0, 2, 1, 1, 1, 1, 1, 1, 1, 1, + 3, 3, 3, 3, 3, 2, 2, 2, 2, 2, + 3, 1, 1, 1, 1, 4, 5, 6, 4, 4, + 6, 6, 6, 9, 7, 5, 4, 2, 2, 2, + 2, 2, 2, 2, 2, 0, 2, 4, 4, 4, + 4, 0, 3, 4, 7, 3, 1, 2, 4, 5, + 7, 2, 4, 6, 0, 1, 0, 2, 1, 2, + 4, 0, 2, 1, 3, 5, 1, 1, 1, 1, + 1, 1, 1, 1, 2, 2, 0, 3, 0, 2, + 0, 3, 1, 3, 2, 0, 1, 1, 0, 2, + 4, 4, 0, 2, 4, 1, 3, 0, 3, 1, + 3, 3, 5, 0, 5, 2, 1, 1, 3, 1, + 2, 3, 1, 1, 3, 3, 1, 1, 0, 2, + 0, 3, 0, 1, 1, 1, 1, 1, 1, 1, + 0, 1, 0, 1, 1, 0, 2, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, @@ -871,147 +910,149 @@ var yyR2 = [...]int{ } var yyChk = [...]int{ - -1000, -101, -1, -2, -3, -4, -5, -6, -7, -8, - -9, -10, -11, -12, 6, 7, 8, 9, 29, 106, - 107, 109, 108, 110, 119, 120, 121, -102, 148, -15, - 5, -13, -103, -13, -13, -13, -13, 111, -84, 113, - 117, -79, 113, 115, 111, 111, 112, 113, 111, -97, - -97, -97, -2, 19, 20, -18, 37, 38, -14, -79, - -36, -34, -92, 53, -87, 141, 122, 26, 146, 124, - 143, 36, 17, 145, 35, 147, 113, 144, 10, -76, - -77, -59, -88, -92, 53, -87, -81, 116, 112, -34, - 111, -34, -34, -80, 116, 53, -80, -34, -16, 33, - 20, 61, -35, 25, -34, 29, -95, -94, 21, -92, - 55, 103, -34, 51, 75, 103, -34, 67, 53, -98, - -104, -89, 50, -88, -86, 53, -87, 66, 21, 23, - 69, 101, 15, 70, 100, 136, 106, 45, 128, 129, - 126, 127, 28, 9, 24, 120, 20, 94, 108, 73, - 74, 123, 22, 121, 64, 18, 48, 10, 12, 13, - 116, 115, 85, 112, 43, 7, 102, 25, 82, 39, - 27, 41, 83, 16, 130, 131, 30, 140, 96, 46, - 33, 67, 62, 49, 65, 14, 44, 84, 109, 135, - 42, 6, 139, 29, 119, 40, 111, 72, 114, 63, - 5, 117, 8, 47, 118, 132, 133, 134, 31, 71, - 11, -34, -98, 114, -34, 22, 49, -34, -17, 40, - -46, -88, 56, 59, -74, 29, -104, -34, -76, -37, - 47, 115, 48, -94, -93, -92, -86, -39, 11, -77, - -25, -40, 67, -45, -41, 22, -44, -59, -57, -58, - 101, 90, 91, 98, 68, 102, -49, -47, -48, -50, - 63, 64, 55, 54, 56, 57, 58, 59, 62, -88, - -92, -55, -104, 41, 42, 136, 137, 140, 138, 70, - 31, 126, 134, 133, 132, 130, 131, 128, 129, 116, - 127, 96, 135, -89, -93, -86, -98, 22, -85, 118, - -82, 109, 107, 28, 108, 14, 147, 53, -34, -34, - -98, -22, -24, 92, -25, -92, -78, 18, 10, 31, - 31, -43, 31, -2, -76, -73, -88, -39, 112, 112, - 112, -66, 14, -25, 66, 65, 82, -25, -42, 85, - 67, 83, 84, 69, 87, 86, 97, 90, 91, 92, - 93, 94, 95, 96, 88, 89, 100, 75, 76, 77, - 78, 79, 80, 81, -58, -104, 104, 105, -45, -45, - -45, -45, -45, -45, -104, 103, -2, -53, -25, -104, - -104, -104, -104, -104, -104, -62, -25, -104, -106, -104, - -106, -106, -106, -106, -106, -106, -106, -104, -104, -104, - -104, 103, 49, -88, -97, -83, 114, -26, 51, 10, - -91, -90, 21, -88, 55, 103, -34, -75, 49, -54, - -56, -55, -104, -75, -105, 51, 52, 103, -66, -104, - -104, -104, -70, 16, 15, -25, -25, -51, 62, 67, - 63, 64, -41, -45, -52, -55, -58, 60, 85, 83, - 84, 69, -45, -45, -45, -45, -45, -45, -45, -45, - -45, -45, -45, -45, -45, -45, -45, -99, 53, 55, - 101, 122, -44, -44, -88, -23, 20, -22, -89, -105, - 51, -105, -22, -22, -25, -25, -72, -88, -16, -60, - -61, 71, -88, -105, -22, -23, -22, -22, -89, -34, - -34, -39, -24, -27, -28, -29, -30, -36, -58, -104, - -90, 92, -93, 26, 51, -105, -88, -88, -70, -38, - -88, -38, -38, -25, -67, -68, -25, 62, 63, 64, - -52, -45, -45, -45, -21, 123, 66, -105, -22, -104, - -25, -105, -105, 51, 118, 21, -105, 51, -22, -63, - -61, 73, -25, -105, -105, -105, -105, -105, -98, -64, - 12, 51, -31, -32, -33, 39, 43, 45, 40, 41, - 42, 46, -96, 21, -27, 103, 27, -56, 103, -105, - 51, -105, -105, 51, 17, 51, -69, 23, 24, -21, - 66, -45, -45, -105, -23, -100, -99, -100, -100, 141, - -88, -66, 74, -25, 72, -65, 13, 15, -28, -29, - -28, -29, 39, 39, 39, 44, 39, 44, 39, -32, - -92, -105, 92, 8, -88, -88, -25, -25, -68, -45, - -105, -105, 124, -104, -99, 125, -105, -105, -104, -20, - 139, -25, -66, -25, -53, 49, 49, 39, 39, -76, - 56, 29, -45, -105, 55, -70, -25, -25, -105, 51, - -99, -19, 85, 144, -71, 18, 30, -99, 125, 56, - -105, 142, 46, 145, 8, 85, 29, -105, 36, 143, - 146, 35, -99, 36, 36, 144, 145, 146, + -1000, -103, -1, -2, -3, -4, -5, -6, -7, -8, + -9, -10, -11, -12, -13, 6, 7, 8, 9, 29, + 106, 107, 109, 108, 110, 119, 120, 121, -104, 153, + -16, 5, -14, -105, -14, -14, -14, -14, 111, -85, + 113, 117, -89, 53, -88, 146, 122, 26, 151, 129, + 148, 36, 17, 150, 35, 152, 149, -80, 113, 115, + 111, 111, 112, 113, 111, -102, 53, -87, -88, 66, + 21, 23, 69, 101, 15, 70, 100, 141, 106, 45, + 133, 134, 131, 132, 124, 28, 9, 24, 120, 20, + 94, 108, 73, 74, 123, 22, 121, 64, 18, 48, + 10, 12, 13, 116, 115, 85, 112, 43, 7, 102, + 25, 82, 39, 27, 41, 83, 16, 135, 136, 30, + 145, 96, 46, 33, 67, 62, 49, 65, 14, 44, + 84, 109, 140, 42, 6, 144, 29, 126, 127, 128, + 119, 40, 111, 125, 72, 114, 63, 5, 117, 8, + 47, 118, 137, 138, 139, 31, 71, 11, 113, -98, + -98, -2, 19, 20, -19, 37, 38, -15, -80, -37, + -35, -93, 53, -88, 10, -77, -78, -60, -89, -93, + 53, -88, -82, 116, 112, -35, 111, -35, -35, -81, + 116, 53, -81, -35, -98, -17, 33, 20, 61, -36, + 25, -35, 29, -96, -95, 21, -93, 55, 103, -35, + 51, 75, 103, -35, 67, 53, -99, -106, -90, 50, + -89, -87, -35, -99, 114, -35, 22, 49, -35, -18, + 40, -47, -89, 56, 59, -75, 29, -106, -35, -77, + -38, 47, 115, 48, -95, -94, -93, -87, -40, 11, + -78, -26, -41, 67, -46, -42, 22, -45, -60, -58, + -59, 101, 90, 91, 98, 68, 102, -50, -48, -49, + -51, 63, 64, 55, 54, 56, 57, 58, 59, 62, + -89, -93, -56, -106, 41, 42, 141, 142, 145, 143, + 70, 31, 131, 139, 138, 137, 135, 136, 133, 134, + 116, 132, 96, 140, -90, -94, -87, -99, 22, -86, + 118, -83, 109, 107, 28, 108, 14, 152, 53, -35, + -35, -99, -23, -25, 92, -26, -93, -79, 18, 10, + 31, 31, -44, 31, -2, -77, -74, -89, -40, 112, + 112, 112, -67, 14, -26, 66, 65, 82, -26, -43, + 85, 67, 83, 84, 69, 87, 86, 97, 90, 91, + 92, 93, 94, 95, 96, 88, 89, 100, 75, 76, + 77, 78, 79, 80, 81, -59, -106, 104, 105, -46, + -46, -46, -46, -46, -46, -106, 103, -2, -54, -26, + -106, -106, -106, -106, -106, -106, -63, -26, -106, -108, + -106, -108, -108, -108, -108, -108, -108, -108, -106, -106, + -106, -106, 103, 49, -89, -98, -84, 114, -27, 51, + 10, -92, -91, 21, -89, 55, 103, -35, -76, 49, + -55, -57, -56, -106, -76, -107, 51, 52, 103, -67, + -106, -106, -106, -71, 16, 15, -26, -26, -52, 62, + 67, 63, 64, -42, -46, -53, -56, -59, 60, 85, + 83, 84, 69, -46, -46, -46, -46, -46, -46, -46, + -46, -46, -46, -46, -46, -46, -46, -46, -100, 53, + 55, 101, 122, -45, -45, -89, -24, 20, -23, -90, + -107, 51, -107, -23, -23, -26, -26, -73, -89, -17, + -61, -62, 71, -89, -107, -23, -24, -23, -23, -90, + -35, -35, -40, -25, -28, -29, -30, -31, -37, -59, + -106, -91, 92, -94, 26, 51, -107, -89, -89, -71, + -39, -89, -39, -39, -26, -68, -69, -26, 62, 63, + 64, -53, -46, -46, -46, -22, 123, 66, -107, -23, + -106, -26, -107, -107, 51, 118, 21, -107, 51, -23, + -64, -62, 73, -26, -107, -107, -107, -107, -107, -99, + -65, 12, 51, -32, -33, -34, 39, 43, 45, 40, + 41, 42, 46, -97, 21, -28, 103, 27, -57, 103, + -107, 51, -107, -107, 51, 17, 51, -70, 23, 24, + -22, 66, -46, -46, -107, -24, -101, -100, -101, -101, + 146, -89, -67, 74, -26, 72, -66, 13, 15, -29, + -30, -29, -30, 39, 39, 39, 44, 39, 44, 39, + -33, -93, -107, 92, 8, -89, -89, -26, -26, -69, + -46, -107, -107, 129, -106, -100, 130, -107, -107, -106, + -21, 144, -26, -67, -26, -54, 49, 49, 39, 39, + -77, 56, 29, -46, -107, 55, -71, -26, -26, -107, + 51, -100, -20, 85, 149, -72, 18, 30, -100, 130, + 56, -107, 147, 46, 150, 8, 85, 29, -107, 36, + 148, 151, 35, -100, 36, 36, 149, 150, 151, } var yyDef = [...]int{ 0, -2, 2, 4, 5, 6, 7, 8, 9, 10, - 11, 12, 13, 14, 37, 37, 37, 37, 37, 298, - 288, 0, 0, 0, 409, 409, 409, 1, 3, 0, - 41, 44, 39, 288, 0, 0, 0, 286, 0, 0, - 299, 0, 0, 289, 0, 284, 0, 284, 0, 34, - 35, 36, 17, 42, 43, 47, 45, 46, 38, 0, - 0, 80, 97, 306, 307, 394, 395, 396, 397, 398, - 399, 400, 401, 402, 403, 404, 405, 406, 0, 22, - 279, 0, 229, 0, -2, -2, 0, 0, 0, 410, - 0, 410, 0, 0, 0, 0, 0, 33, 49, 0, - 48, 40, 263, 0, 96, 0, 99, 81, 0, 83, - 84, 0, 105, 0, 0, 0, 410, 0, 300, 25, - 411, 412, 407, 304, 305, 302, 303, 310, 311, 312, - 313, 314, 315, 316, 317, 318, 319, 320, 321, 322, - 323, 324, 325, 326, 327, 328, 329, 330, 331, 332, - 333, 334, 335, 336, 337, 338, 339, 340, 341, 342, - 343, 344, 345, 346, 347, 348, 349, 350, 351, 352, - 353, 354, 355, 356, 357, 358, 359, 360, 361, 362, - 363, 364, 365, 366, 367, 368, 369, 370, 371, 372, - 373, 374, 375, 376, 377, 378, 379, 380, 381, 382, - 383, 384, 385, 386, 387, 388, 389, 390, 391, 392, - 393, 0, 28, 0, 30, 285, 0, 410, 0, 50, - 0, 239, 0, 0, 0, 0, 0, 95, 105, 73, - 0, 0, 0, 82, 98, 308, 309, 246, 0, 280, - 281, 107, 0, 112, 115, 0, 152, 153, 154, 155, - 0, 0, 0, 0, 0, 0, 177, 178, 179, 180, - 113, 114, 232, 233, 234, 235, 236, 237, 238, 229, - 0, 278, 0, 0, 0, 0, 0, 0, 0, 220, - 0, 201, 201, 201, 201, 201, 201, 201, 201, 0, - 0, 0, 0, 230, 0, -2, 23, 287, 0, 0, - 409, 296, 290, 291, 292, 293, 294, 295, 29, 31, - 32, 64, 53, 55, 59, 0, 0, 282, 283, 240, - 241, 269, 0, 272, 269, 0, 265, 246, 0, 0, - 0, 254, 0, 106, 0, 0, 0, 110, 0, 0, + 11, 12, 13, 14, 15, 41, 41, 41, 41, 41, + 302, 292, 0, 0, 0, 0, 419, 419, 1, 3, + 0, 45, 48, 43, 292, 0, 0, 0, 290, 0, + 415, 303, 304, 307, 308, 404, 405, 406, 407, 408, + 409, 410, 411, 412, 413, 414, 416, 0, 0, 293, + 0, 288, 0, 288, 0, 419, 35, 36, 37, 315, + 316, 317, 318, 319, 320, 321, 322, 323, 324, 325, + 326, 327, 328, 329, 330, 331, 332, 333, 334, 335, + 336, 337, 338, 339, 340, 341, 342, 343, 344, 345, + 346, 347, 348, 349, 350, 351, 352, 353, 354, 355, + 356, 357, 358, 359, 360, 361, 362, 363, 364, 365, + 366, 367, 368, 369, 370, 371, 372, 373, 374, 375, + 376, 377, 378, 379, 380, 381, 382, 383, 384, 385, + 386, 387, 388, 389, 390, 391, 392, 393, 394, 395, + 396, 397, 398, 399, 400, 401, 402, 403, 415, 39, + 40, 18, 46, 47, 51, 49, 50, 42, 0, 0, + 84, 101, 311, 312, 0, 23, 283, 0, 233, 0, + -2, -2, 0, 0, 0, 420, 0, 420, 0, 0, + 0, 0, 0, 34, 38, 53, 0, 52, 44, 267, + 0, 100, 0, 103, 85, 0, 87, 88, 0, 109, + 0, 0, 0, 420, 0, 305, 26, 421, 422, 417, + 309, 310, 0, 29, 0, 31, 289, 0, 420, 0, + 54, 0, 243, 0, 0, 0, 0, 0, 99, 109, + 77, 0, 0, 0, 86, 102, 313, 314, 250, 0, + 284, 285, 111, 0, 116, 119, 0, 156, 157, 158, + 159, 0, 0, 0, 0, 0, 0, 181, 182, 183, + 184, 117, 118, 236, 237, 238, 239, 240, 241, 242, + 233, 0, 282, 0, 0, 0, 0, 0, 0, 0, + 224, 0, 205, 205, 205, 205, 205, 205, 205, 205, + 0, 0, 0, 0, 234, 0, -2, 24, 291, 0, + 0, 419, 300, 294, 295, 296, 297, 298, 299, 30, + 32, 33, 68, 57, 59, 63, 0, 0, 286, 287, + 244, 245, 273, 0, 276, 273, 0, 269, 250, 0, + 0, 0, 258, 0, 110, 0, 0, 0, 114, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 133, 134, 135, - 136, 137, 138, 139, 126, 0, 0, 0, 171, 172, - 173, 174, 175, 0, 51, 0, 0, 0, 146, 0, - 0, 0, 0, 0, 47, 0, 221, 0, 193, 0, - 194, 195, 196, 197, 198, 199, 200, 0, 51, 0, - 0, 0, 0, 301, 26, 0, 297, 105, 0, 0, - 56, 60, 0, 62, 63, 0, 16, 18, 0, 271, - 273, 275, 0, 19, 264, 0, 408, 0, 254, 0, - 0, 0, 21, 0, 0, 108, 109, 111, 127, 0, - 129, 131, 116, 117, 118, 142, 143, 144, 0, 0, - 0, 0, 140, 122, 0, 156, 157, 158, 159, 160, - 161, 162, 163, 164, 165, 166, 167, 170, 148, 149, - 150, 151, 168, 169, 176, 0, 0, 52, 230, 145, - 0, 277, 0, 0, 0, 0, 0, 261, 0, 227, - 224, 0, 0, 202, 0, 0, 0, 0, 231, 410, - 27, 242, 54, 65, 66, 68, 69, 70, 78, 0, - 61, 57, 0, 0, 0, 276, 267, 266, 20, 0, - 103, 0, 0, 255, 247, 248, 251, 128, 130, 132, - 119, 140, 123, 0, 120, 0, 0, 181, 0, 51, - 147, 184, 185, 0, 0, 0, 0, 0, 246, 0, - 225, 0, 0, 192, 203, 204, 205, 206, 24, 244, - 0, 0, 0, 0, 0, 85, 0, 0, 88, 0, - 0, 0, 0, 79, 0, 0, 0, 274, 0, 100, - 0, 101, 102, 0, 0, 0, 250, 252, 253, 121, - 0, 141, 124, 182, 0, 0, 212, 0, 0, 0, - 262, 222, 191, 228, 0, 246, 0, 0, 67, 74, - 0, 77, 86, 87, 89, 0, 91, 0, 93, 94, - 71, 72, 58, 0, 268, 104, 256, 257, 249, 125, - 183, 186, 213, 0, 217, 0, 187, 188, 0, 0, - 0, 226, 254, 245, 243, 0, 0, 90, 92, 270, - 0, 0, 207, 190, 223, 258, 75, 76, 214, 0, - 218, 0, 0, 0, 15, 0, 0, 215, 0, 0, - 189, 0, 0, 0, 259, 0, 0, 219, 208, 0, - 211, 0, 216, 209, 260, 0, 0, 210, + 0, 0, 0, 0, 0, 0, 0, 0, 137, 138, + 139, 140, 141, 142, 143, 130, 0, 0, 0, 175, + 176, 177, 178, 179, 0, 55, 0, 0, 0, 150, + 0, 0, 0, 0, 0, 51, 0, 225, 0, 197, + 0, 198, 199, 200, 201, 202, 203, 204, 0, 55, + 0, 0, 0, 0, 306, 27, 0, 301, 109, 0, + 0, 60, 64, 0, 66, 67, 0, 17, 19, 0, + 275, 277, 279, 0, 20, 268, 0, 418, 0, 258, + 0, 0, 0, 22, 0, 0, 112, 113, 115, 131, + 0, 133, 135, 120, 121, 122, 146, 147, 148, 0, + 0, 0, 0, 144, 126, 0, 160, 161, 162, 163, + 164, 165, 166, 167, 168, 169, 170, 171, 174, 152, + 153, 154, 155, 172, 173, 180, 0, 0, 56, 234, + 149, 0, 281, 0, 0, 0, 0, 0, 265, 0, + 231, 228, 0, 0, 206, 0, 0, 0, 0, 235, + 420, 28, 246, 58, 69, 70, 72, 73, 74, 82, + 0, 65, 61, 0, 0, 0, 280, 271, 270, 21, + 0, 107, 0, 0, 259, 251, 252, 255, 132, 134, + 136, 123, 144, 127, 0, 124, 0, 0, 185, 0, + 55, 151, 188, 189, 0, 0, 0, 0, 0, 250, + 0, 229, 0, 0, 196, 207, 208, 209, 210, 25, + 248, 0, 0, 0, 0, 0, 89, 0, 0, 92, + 0, 0, 0, 0, 83, 0, 0, 0, 278, 0, + 104, 0, 105, 106, 0, 0, 0, 254, 256, 257, + 125, 0, 145, 128, 186, 0, 0, 216, 0, 0, + 0, 266, 226, 195, 232, 0, 250, 0, 0, 71, + 78, 0, 81, 90, 91, 93, 0, 95, 0, 97, + 98, 75, 76, 62, 0, 272, 108, 260, 261, 253, + 129, 187, 190, 217, 0, 221, 0, 191, 192, 0, + 0, 0, 230, 258, 249, 247, 0, 0, 94, 96, + 274, 0, 0, 211, 194, 227, 262, 79, 80, 218, + 0, 222, 0, 0, 0, 16, 0, 0, 219, 0, + 0, 193, 0, 0, 0, 263, 0, 0, 223, 212, + 0, 215, 0, 220, 213, 264, 0, 0, 214, } var yyTok1 = [...]int{ @@ -1020,7 +1061,7 @@ var yyTok1 = [...]int{ 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 68, 3, 3, 3, 95, 87, 3, 50, 52, 92, 90, 51, 91, 103, 93, 3, 3, - 3, 3, 3, 3, 3, 3, 3, 3, 3, 148, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 153, 76, 75, 77, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, @@ -1043,7 +1084,8 @@ var yyTok2 = [...]int{ 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, 127, 128, 129, 130, 131, 132, 133, 134, 135, 136, 137, 138, - 139, 140, 141, 142, 143, 144, 145, 146, 147, + 139, 140, 141, 142, 143, 144, 145, 146, 147, 148, + 149, 150, 151, 152, } var yyTok3 = [...]int{ 0, @@ -1388,53 +1430,53 @@ yydefault: case 1: yyDollar = yyS[yypt-2 : yypt+1] - //line ./go/vt/sqlparser/sql.y:199 + //line ./go/vt/sqlparser/sql.y:202 { setParseTree(yylex, yyDollar[1].statement) } case 2: yyDollar = yyS[yypt-0 : yypt+1] - //line ./go/vt/sqlparser/sql.y:204 + //line ./go/vt/sqlparser/sql.y:207 { } case 3: yyDollar = yyS[yypt-1 : yypt+1] - //line ./go/vt/sqlparser/sql.y:205 + //line ./go/vt/sqlparser/sql.y:208 { } case 4: yyDollar = yyS[yypt-1 : yypt+1] - //line ./go/vt/sqlparser/sql.y:209 + //line ./go/vt/sqlparser/sql.y:212 { yyVAL.statement = yyDollar[1].selStmt } - case 15: + case 16: yyDollar = yyS[yypt-13 : yypt+1] - //line ./go/vt/sqlparser/sql.y:225 + //line ./go/vt/sqlparser/sql.y:229 { yyVAL.selStmt = &Select{Comments: Comments(yyDollar[2].bytes2), Cache: yyDollar[3].str, Distinct: yyDollar[4].str, Hints: yyDollar[5].str, SelectExprs: yyDollar[6].selectExprs, From: yyDollar[7].tableExprs, Where: NewWhere(WhereStr, yyDollar[8].expr), GroupBy: GroupBy(yyDollar[9].exprs), Having: NewWhere(HavingStr, yyDollar[10].expr), OrderBy: yyDollar[11].orderBy, Limit: yyDollar[12].limit, Lock: yyDollar[13].str} } - case 16: + case 17: yyDollar = yyS[yypt-7 : yypt+1] - //line ./go/vt/sqlparser/sql.y:229 + //line ./go/vt/sqlparser/sql.y:233 { yyVAL.selStmt = &Select{Comments: Comments(yyDollar[2].bytes2), Cache: yyDollar[3].str, SelectExprs: SelectExprs{Nextval{Expr: yyDollar[5].expr}}, From: TableExprs{&AliasedTableExpr{Expr: yyDollar[7].tableName}}} } - case 17: + case 18: yyDollar = yyS[yypt-3 : yypt+1] - //line ./go/vt/sqlparser/sql.y:233 + //line ./go/vt/sqlparser/sql.y:237 { yyVAL.selStmt = &Union{Type: yyDollar[2].str, Left: yyDollar[1].selStmt, Right: yyDollar[3].selStmt} } - case 18: + case 19: yyDollar = yyS[yypt-7 : yypt+1] - //line ./go/vt/sqlparser/sql.y:239 + //line ./go/vt/sqlparser/sql.y:243 { yyVAL.statement = &Insert{Comments: Comments(yyDollar[2].bytes2), Ignore: yyDollar[3].str, Table: yyDollar[4].tableName, Columns: yyDollar[5].columns, Rows: yyDollar[6].insRows, OnDup: OnDup(yyDollar[7].updateExprs)} } - case 19: + case 20: yyDollar = yyS[yypt-7 : yypt+1] - //line ./go/vt/sqlparser/sql.y:243 + //line ./go/vt/sqlparser/sql.y:247 { cols := make(Columns, 0, len(yyDollar[6].updateExprs)) vals := make(ValTuple, 0, len(yyDollar[7].updateExprs)) @@ -1444,71 +1486,71 @@ yydefault: } yyVAL.statement = &Insert{Comments: Comments(yyDollar[2].bytes2), Ignore: yyDollar[3].str, Table: yyDollar[4].tableName, Columns: cols, Rows: Values{vals}, OnDup: OnDup(yyDollar[7].updateExprs)} } - case 20: + case 21: yyDollar = yyS[yypt-8 : yypt+1] - //line ./go/vt/sqlparser/sql.y:255 + //line ./go/vt/sqlparser/sql.y:259 { yyVAL.statement = &Update{Comments: Comments(yyDollar[2].bytes2), Table: yyDollar[3].aliasedTableName, Exprs: yyDollar[5].updateExprs, Where: NewWhere(WhereStr, yyDollar[6].expr), OrderBy: yyDollar[7].orderBy, Limit: yyDollar[8].limit} } - case 21: + case 22: yyDollar = yyS[yypt-7 : yypt+1] - //line ./go/vt/sqlparser/sql.y:261 + //line ./go/vt/sqlparser/sql.y:265 { yyVAL.statement = &Delete{Comments: Comments(yyDollar[2].bytes2), Table: yyDollar[4].tableName, Where: NewWhere(WhereStr, yyDollar[5].expr), OrderBy: yyDollar[6].orderBy, Limit: yyDollar[7].limit} } - case 22: + case 23: yyDollar = yyS[yypt-3 : yypt+1] - //line ./go/vt/sqlparser/sql.y:267 + //line ./go/vt/sqlparser/sql.y:271 { yyVAL.statement = &Set{Comments: Comments(yyDollar[2].bytes2), Exprs: yyDollar[3].updateExprs} } - case 23: + case 24: yyDollar = yyS[yypt-5 : yypt+1] - //line ./go/vt/sqlparser/sql.y:273 + //line ./go/vt/sqlparser/sql.y:277 { yyVAL.statement = &DDL{Action: CreateStr, NewName: yyDollar[4].tableName} } - case 24: + case 25: yyDollar = yyS[yypt-8 : yypt+1] - //line ./go/vt/sqlparser/sql.y:277 + //line ./go/vt/sqlparser/sql.y:281 { // Change this to an alter statement yyVAL.statement = &DDL{Action: AlterStr, Table: yyDollar[7].tableName, NewName: yyDollar[7].tableName} } - case 25: + case 26: yyDollar = yyS[yypt-4 : yypt+1] - //line ./go/vt/sqlparser/sql.y:282 + //line ./go/vt/sqlparser/sql.y:286 { yyVAL.statement = &DDL{Action: CreateStr, NewName: yyDollar[3].tableName.ToViewName()} } - case 26: + case 27: yyDollar = yyS[yypt-6 : yypt+1] - //line ./go/vt/sqlparser/sql.y:288 + //line ./go/vt/sqlparser/sql.y:292 { yyVAL.statement = &DDL{Action: AlterStr, Table: yyDollar[4].tableName, NewName: yyDollar[4].tableName} } - case 27: + case 28: yyDollar = yyS[yypt-7 : yypt+1] - //line ./go/vt/sqlparser/sql.y:292 + //line ./go/vt/sqlparser/sql.y:296 { // Change this to a rename statement yyVAL.statement = &DDL{Action: RenameStr, Table: yyDollar[4].tableName, NewName: yyDollar[7].tableName} } - case 28: + case 29: yyDollar = yyS[yypt-4 : yypt+1] - //line ./go/vt/sqlparser/sql.y:297 + //line ./go/vt/sqlparser/sql.y:301 { yyVAL.statement = &DDL{Action: AlterStr, Table: yyDollar[3].tableName.ToViewName(), NewName: yyDollar[3].tableName.ToViewName()} } - case 29: + case 30: yyDollar = yyS[yypt-5 : yypt+1] - //line ./go/vt/sqlparser/sql.y:303 + //line ./go/vt/sqlparser/sql.y:307 { yyVAL.statement = &DDL{Action: RenameStr, Table: yyDollar[3].tableName, NewName: yyDollar[5].tableName} } - case 30: + case 31: yyDollar = yyS[yypt-4 : yypt+1] - //line ./go/vt/sqlparser/sql.y:309 + //line ./go/vt/sqlparser/sql.y:313 { var exists bool if yyDollar[3].byt != 0 { @@ -1516,16 +1558,16 @@ yydefault: } yyVAL.statement = &DDL{Action: DropStr, Table: yyDollar[4].tableName, IfExists: exists} } - case 31: + case 32: yyDollar = yyS[yypt-5 : yypt+1] - //line ./go/vt/sqlparser/sql.y:317 + //line ./go/vt/sqlparser/sql.y:321 { // Change this to an alter statement yyVAL.statement = &DDL{Action: AlterStr, Table: yyDollar[5].tableName, NewName: yyDollar[5].tableName} } - case 32: + case 33: yyDollar = yyS[yypt-5 : yypt+1] - //line ./go/vt/sqlparser/sql.y:322 + //line ./go/vt/sqlparser/sql.y:326 { var exists bool if yyDollar[3].byt != 0 { @@ -1533,352 +1575,382 @@ yydefault: } yyVAL.statement = &DDL{Action: DropStr, Table: yyDollar[4].tableName.ToViewName(), IfExists: exists} } - case 33: + case 34: yyDollar = yyS[yypt-3 : yypt+1] - //line ./go/vt/sqlparser/sql.y:332 + //line ./go/vt/sqlparser/sql.y:336 { yyVAL.statement = &DDL{Action: AlterStr, Table: yyDollar[3].tableName, NewName: yyDollar[3].tableName} } - case 34: - yyDollar = yyS[yypt-2 : yypt+1] - //line ./go/vt/sqlparser/sql.y:338 + case 35: + yyDollar = yyS[yypt-1 : yypt+1] + //line ./go/vt/sqlparser/sql.y:342 { - yyVAL.statement = &Other{} + yyVAL.str = ShowUnsupportedStr } - case 35: + case 36: + yyDollar = yyS[yypt-1 : yypt+1] + //line ./go/vt/sqlparser/sql.y:346 + { + if string(yyDollar[1].bytes) == "databases" { + yyVAL.str = ShowDatabasesStr + } else if string(yyDollar[1].bytes) == "tables" { + yyVAL.str = ShowTablesStr + } else if string(yyDollar[1].bytes) == "vitess_keyspaces" { + yyVAL.str = ShowKeyspacesStr + } else if string(yyDollar[1].bytes) == "vitess_shards" { + yyVAL.str = ShowShardsStr + } else if string(yyDollar[1].bytes) == "vschema_tables" { + yyVAL.str = ShowVSchemaTablesStr + } else { + yyVAL.str = ShowUnsupportedStr + } + } + case 37: + yyDollar = yyS[yypt-1 : yypt+1] + //line ./go/vt/sqlparser/sql.y:362 + { + yyVAL.str = ShowUnsupportedStr + } + case 38: + yyDollar = yyS[yypt-3 : yypt+1] + //line ./go/vt/sqlparser/sql.y:368 + { + yyVAL.statement = &Show{Type: yyDollar[2].str} + } + case 39: yyDollar = yyS[yypt-2 : yypt+1] - //line ./go/vt/sqlparser/sql.y:342 + //line ./go/vt/sqlparser/sql.y:374 { yyVAL.statement = &Other{} } - case 36: + case 40: yyDollar = yyS[yypt-2 : yypt+1] - //line ./go/vt/sqlparser/sql.y:346 + //line ./go/vt/sqlparser/sql.y:378 { yyVAL.statement = &Other{} } - case 37: + case 41: yyDollar = yyS[yypt-0 : yypt+1] - //line ./go/vt/sqlparser/sql.y:351 + //line ./go/vt/sqlparser/sql.y:383 { setAllowComments(yylex, true) } - case 38: + case 42: yyDollar = yyS[yypt-2 : yypt+1] - //line ./go/vt/sqlparser/sql.y:355 + //line ./go/vt/sqlparser/sql.y:387 { yyVAL.bytes2 = yyDollar[2].bytes2 setAllowComments(yylex, false) } - case 39: + case 43: yyDollar = yyS[yypt-0 : yypt+1] - //line ./go/vt/sqlparser/sql.y:361 + //line ./go/vt/sqlparser/sql.y:393 { yyVAL.bytes2 = nil } - case 40: + case 44: yyDollar = yyS[yypt-2 : yypt+1] - //line ./go/vt/sqlparser/sql.y:365 + //line ./go/vt/sqlparser/sql.y:397 { yyVAL.bytes2 = append(yyDollar[1].bytes2, yyDollar[2].bytes) } - case 41: + case 45: yyDollar = yyS[yypt-1 : yypt+1] - //line ./go/vt/sqlparser/sql.y:371 + //line ./go/vt/sqlparser/sql.y:403 { yyVAL.str = UnionStr } - case 42: + case 46: yyDollar = yyS[yypt-2 : yypt+1] - //line ./go/vt/sqlparser/sql.y:375 + //line ./go/vt/sqlparser/sql.y:407 { yyVAL.str = UnionAllStr } - case 43: + case 47: yyDollar = yyS[yypt-2 : yypt+1] - //line ./go/vt/sqlparser/sql.y:379 + //line ./go/vt/sqlparser/sql.y:411 { yyVAL.str = UnionDistinctStr } - case 44: + case 48: yyDollar = yyS[yypt-0 : yypt+1] - //line ./go/vt/sqlparser/sql.y:384 + //line ./go/vt/sqlparser/sql.y:416 { yyVAL.str = "" } - case 45: + case 49: yyDollar = yyS[yypt-1 : yypt+1] - //line ./go/vt/sqlparser/sql.y:388 + //line ./go/vt/sqlparser/sql.y:420 { yyVAL.str = SQLNoCacheStr } - case 46: + case 50: yyDollar = yyS[yypt-1 : yypt+1] - //line ./go/vt/sqlparser/sql.y:392 + //line ./go/vt/sqlparser/sql.y:424 { yyVAL.str = SQLCacheStr } - case 47: + case 51: yyDollar = yyS[yypt-0 : yypt+1] - //line ./go/vt/sqlparser/sql.y:397 + //line ./go/vt/sqlparser/sql.y:429 { yyVAL.str = "" } - case 48: + case 52: yyDollar = yyS[yypt-1 : yypt+1] - //line ./go/vt/sqlparser/sql.y:401 + //line ./go/vt/sqlparser/sql.y:433 { yyVAL.str = DistinctStr } - case 49: + case 53: yyDollar = yyS[yypt-0 : yypt+1] - //line ./go/vt/sqlparser/sql.y:406 + //line ./go/vt/sqlparser/sql.y:438 { yyVAL.str = "" } - case 50: + case 54: yyDollar = yyS[yypt-1 : yypt+1] - //line ./go/vt/sqlparser/sql.y:410 + //line ./go/vt/sqlparser/sql.y:442 { yyVAL.str = StraightJoinHint } - case 51: + case 55: yyDollar = yyS[yypt-0 : yypt+1] - //line ./go/vt/sqlparser/sql.y:415 + //line ./go/vt/sqlparser/sql.y:447 { yyVAL.selectExprs = nil } - case 52: + case 56: yyDollar = yyS[yypt-1 : yypt+1] - //line ./go/vt/sqlparser/sql.y:419 + //line ./go/vt/sqlparser/sql.y:451 { yyVAL.selectExprs = yyDollar[1].selectExprs } - case 53: + case 57: yyDollar = yyS[yypt-1 : yypt+1] - //line ./go/vt/sqlparser/sql.y:425 + //line ./go/vt/sqlparser/sql.y:457 { yyVAL.selectExprs = SelectExprs{yyDollar[1].selectExpr} } - case 54: + case 58: yyDollar = yyS[yypt-3 : yypt+1] - //line ./go/vt/sqlparser/sql.y:429 + //line ./go/vt/sqlparser/sql.y:461 { yyVAL.selectExprs = append(yyVAL.selectExprs, yyDollar[3].selectExpr) } - case 55: + case 59: yyDollar = yyS[yypt-1 : yypt+1] - //line ./go/vt/sqlparser/sql.y:435 + //line ./go/vt/sqlparser/sql.y:467 { yyVAL.selectExpr = &StarExpr{} } - case 56: + case 60: yyDollar = yyS[yypt-2 : yypt+1] - //line ./go/vt/sqlparser/sql.y:439 + //line ./go/vt/sqlparser/sql.y:471 { yyVAL.selectExpr = &NonStarExpr{Expr: yyDollar[1].expr, As: yyDollar[2].colIdent} } - case 57: + case 61: yyDollar = yyS[yypt-3 : yypt+1] - //line ./go/vt/sqlparser/sql.y:443 + //line ./go/vt/sqlparser/sql.y:475 { yyVAL.selectExpr = &StarExpr{TableName: &TableName{Name: yyDollar[1].tableIdent}} } - case 58: + case 62: yyDollar = yyS[yypt-5 : yypt+1] - //line ./go/vt/sqlparser/sql.y:447 + //line ./go/vt/sqlparser/sql.y:479 { yyVAL.selectExpr = &StarExpr{TableName: &TableName{Qualifier: yyDollar[1].tableIdent, Name: yyDollar[3].tableIdent}} } - case 59: + case 63: yyDollar = yyS[yypt-0 : yypt+1] - //line ./go/vt/sqlparser/sql.y:452 + //line ./go/vt/sqlparser/sql.y:484 { yyVAL.colIdent = ColIdent{} } - case 60: + case 64: yyDollar = yyS[yypt-1 : yypt+1] - //line ./go/vt/sqlparser/sql.y:456 + //line ./go/vt/sqlparser/sql.y:488 { yyVAL.colIdent = yyDollar[1].colIdent } - case 61: + case 65: yyDollar = yyS[yypt-2 : yypt+1] - //line ./go/vt/sqlparser/sql.y:460 + //line ./go/vt/sqlparser/sql.y:492 { yyVAL.colIdent = yyDollar[2].colIdent } - case 63: + case 67: yyDollar = yyS[yypt-1 : yypt+1] - //line ./go/vt/sqlparser/sql.y:467 + //line ./go/vt/sqlparser/sql.y:499 { yyVAL.colIdent = NewColIdent(string(yyDollar[1].bytes)) } - case 64: + case 68: yyDollar = yyS[yypt-0 : yypt+1] - //line ./go/vt/sqlparser/sql.y:472 + //line ./go/vt/sqlparser/sql.y:504 { yyVAL.tableExprs = TableExprs{&AliasedTableExpr{Expr: &TableName{Name: NewTableIdent("dual")}}} } - case 65: + case 69: yyDollar = yyS[yypt-2 : yypt+1] - //line ./go/vt/sqlparser/sql.y:476 + //line ./go/vt/sqlparser/sql.y:508 { yyVAL.tableExprs = yyDollar[2].tableExprs } - case 66: + case 70: yyDollar = yyS[yypt-1 : yypt+1] - //line ./go/vt/sqlparser/sql.y:482 + //line ./go/vt/sqlparser/sql.y:514 { yyVAL.tableExprs = TableExprs{yyDollar[1].tableExpr} } - case 67: + case 71: yyDollar = yyS[yypt-3 : yypt+1] - //line ./go/vt/sqlparser/sql.y:486 + //line ./go/vt/sqlparser/sql.y:518 { yyVAL.tableExprs = append(yyVAL.tableExprs, yyDollar[3].tableExpr) } - case 70: + case 74: yyDollar = yyS[yypt-1 : yypt+1] - //line ./go/vt/sqlparser/sql.y:496 + //line ./go/vt/sqlparser/sql.y:528 { yyVAL.tableExpr = yyDollar[1].aliasedTableName } - case 71: + case 75: yyDollar = yyS[yypt-3 : yypt+1] - //line ./go/vt/sqlparser/sql.y:500 + //line ./go/vt/sqlparser/sql.y:532 { yyVAL.tableExpr = &AliasedTableExpr{Expr: yyDollar[1].subquery, As: yyDollar[3].tableIdent} } - case 72: + case 76: yyDollar = yyS[yypt-3 : yypt+1] - //line ./go/vt/sqlparser/sql.y:504 + //line ./go/vt/sqlparser/sql.y:536 { yyVAL.tableExpr = &ParenTableExpr{Exprs: yyDollar[2].tableExprs} } - case 73: + case 77: yyDollar = yyS[yypt-3 : yypt+1] - //line ./go/vt/sqlparser/sql.y:510 + //line ./go/vt/sqlparser/sql.y:542 { yyVAL.aliasedTableName = &AliasedTableExpr{Expr: yyDollar[1].tableName, As: yyDollar[2].tableIdent, Hints: yyDollar[3].indexHints} } - case 74: + case 78: yyDollar = yyS[yypt-3 : yypt+1] - //line ./go/vt/sqlparser/sql.y:523 + //line ./go/vt/sqlparser/sql.y:555 { yyVAL.tableExpr = &JoinTableExpr{LeftExpr: yyDollar[1].tableExpr, Join: yyDollar[2].str, RightExpr: yyDollar[3].tableExpr} } - case 75: + case 79: yyDollar = yyS[yypt-5 : yypt+1] - //line ./go/vt/sqlparser/sql.y:527 + //line ./go/vt/sqlparser/sql.y:559 { yyVAL.tableExpr = &JoinTableExpr{LeftExpr: yyDollar[1].tableExpr, Join: yyDollar[2].str, RightExpr: yyDollar[3].tableExpr, On: yyDollar[5].expr} } - case 76: + case 80: yyDollar = yyS[yypt-5 : yypt+1] - //line ./go/vt/sqlparser/sql.y:531 + //line ./go/vt/sqlparser/sql.y:563 { yyVAL.tableExpr = &JoinTableExpr{LeftExpr: yyDollar[1].tableExpr, Join: yyDollar[2].str, RightExpr: yyDollar[3].tableExpr, On: yyDollar[5].expr} } - case 77: + case 81: yyDollar = yyS[yypt-3 : yypt+1] - //line ./go/vt/sqlparser/sql.y:535 + //line ./go/vt/sqlparser/sql.y:567 { yyVAL.tableExpr = &JoinTableExpr{LeftExpr: yyDollar[1].tableExpr, Join: yyDollar[2].str, RightExpr: yyDollar[3].tableExpr} } - case 78: + case 82: yyDollar = yyS[yypt-0 : yypt+1] - //line ./go/vt/sqlparser/sql.y:540 + //line ./go/vt/sqlparser/sql.y:572 { yyVAL.empty = struct{}{} } - case 79: + case 83: yyDollar = yyS[yypt-1 : yypt+1] - //line ./go/vt/sqlparser/sql.y:542 + //line ./go/vt/sqlparser/sql.y:574 { yyVAL.empty = struct{}{} } - case 80: + case 84: yyDollar = yyS[yypt-0 : yypt+1] - //line ./go/vt/sqlparser/sql.y:545 + //line ./go/vt/sqlparser/sql.y:577 { yyVAL.tableIdent = NewTableIdent("") } - case 81: + case 85: yyDollar = yyS[yypt-1 : yypt+1] - //line ./go/vt/sqlparser/sql.y:549 + //line ./go/vt/sqlparser/sql.y:581 { yyVAL.tableIdent = yyDollar[1].tableIdent } - case 82: + case 86: yyDollar = yyS[yypt-2 : yypt+1] - //line ./go/vt/sqlparser/sql.y:553 + //line ./go/vt/sqlparser/sql.y:585 { yyVAL.tableIdent = yyDollar[2].tableIdent } - case 84: + case 88: yyDollar = yyS[yypt-1 : yypt+1] - //line ./go/vt/sqlparser/sql.y:560 + //line ./go/vt/sqlparser/sql.y:592 { yyVAL.tableIdent = NewTableIdent(string(yyDollar[1].bytes)) } - case 85: + case 89: yyDollar = yyS[yypt-1 : yypt+1] - //line ./go/vt/sqlparser/sql.y:566 + //line ./go/vt/sqlparser/sql.y:598 { yyVAL.str = JoinStr } - case 86: + case 90: yyDollar = yyS[yypt-2 : yypt+1] - //line ./go/vt/sqlparser/sql.y:570 + //line ./go/vt/sqlparser/sql.y:602 { yyVAL.str = JoinStr } - case 87: + case 91: yyDollar = yyS[yypt-2 : yypt+1] - //line ./go/vt/sqlparser/sql.y:574 + //line ./go/vt/sqlparser/sql.y:606 { yyVAL.str = JoinStr } - case 88: + case 92: yyDollar = yyS[yypt-1 : yypt+1] - //line ./go/vt/sqlparser/sql.y:578 + //line ./go/vt/sqlparser/sql.y:610 { yyVAL.str = StraightJoinStr } - case 89: + case 93: yyDollar = yyS[yypt-2 : yypt+1] - //line ./go/vt/sqlparser/sql.y:584 + //line ./go/vt/sqlparser/sql.y:616 { yyVAL.str = LeftJoinStr } - case 90: + case 94: yyDollar = yyS[yypt-3 : yypt+1] - //line ./go/vt/sqlparser/sql.y:588 + //line ./go/vt/sqlparser/sql.y:620 { yyVAL.str = LeftJoinStr } - case 91: + case 95: yyDollar = yyS[yypt-2 : yypt+1] - //line ./go/vt/sqlparser/sql.y:592 + //line ./go/vt/sqlparser/sql.y:624 { yyVAL.str = RightJoinStr } - case 92: + case 96: yyDollar = yyS[yypt-3 : yypt+1] - //line ./go/vt/sqlparser/sql.y:596 + //line ./go/vt/sqlparser/sql.y:628 { yyVAL.str = RightJoinStr } - case 93: + case 97: yyDollar = yyS[yypt-2 : yypt+1] - //line ./go/vt/sqlparser/sql.y:602 + //line ./go/vt/sqlparser/sql.y:634 { yyVAL.str = NaturalJoinStr } - case 94: + case 98: yyDollar = yyS[yypt-2 : yypt+1] - //line ./go/vt/sqlparser/sql.y:606 + //line ./go/vt/sqlparser/sql.y:638 { if yyDollar[2].str == LeftJoinStr { yyVAL.str = NaturalLeftJoinStr @@ -1886,471 +1958,471 @@ yydefault: yyVAL.str = NaturalRightJoinStr } } - case 95: + case 99: yyDollar = yyS[yypt-2 : yypt+1] - //line ./go/vt/sqlparser/sql.y:616 + //line ./go/vt/sqlparser/sql.y:648 { yyVAL.tableName = yyDollar[2].tableName } - case 96: + case 100: yyDollar = yyS[yypt-1 : yypt+1] - //line ./go/vt/sqlparser/sql.y:620 + //line ./go/vt/sqlparser/sql.y:652 { yyVAL.tableName = yyDollar[1].tableName } - case 97: + case 101: yyDollar = yyS[yypt-1 : yypt+1] - //line ./go/vt/sqlparser/sql.y:626 + //line ./go/vt/sqlparser/sql.y:658 { yyVAL.tableName = &TableName{Name: yyDollar[1].tableIdent} } - case 98: + case 102: yyDollar = yyS[yypt-3 : yypt+1] - //line ./go/vt/sqlparser/sql.y:630 + //line ./go/vt/sqlparser/sql.y:662 { yyVAL.tableName = &TableName{Qualifier: yyDollar[1].tableIdent, Name: yyDollar[3].tableIdent} } - case 99: + case 103: yyDollar = yyS[yypt-0 : yypt+1] - //line ./go/vt/sqlparser/sql.y:635 + //line ./go/vt/sqlparser/sql.y:667 { yyVAL.indexHints = nil } - case 100: + case 104: yyDollar = yyS[yypt-5 : yypt+1] - //line ./go/vt/sqlparser/sql.y:639 + //line ./go/vt/sqlparser/sql.y:671 { yyVAL.indexHints = &IndexHints{Type: UseStr, Indexes: yyDollar[4].colIdents} } - case 101: + case 105: yyDollar = yyS[yypt-5 : yypt+1] - //line ./go/vt/sqlparser/sql.y:643 + //line ./go/vt/sqlparser/sql.y:675 { yyVAL.indexHints = &IndexHints{Type: IgnoreStr, Indexes: yyDollar[4].colIdents} } - case 102: + case 106: yyDollar = yyS[yypt-5 : yypt+1] - //line ./go/vt/sqlparser/sql.y:647 + //line ./go/vt/sqlparser/sql.y:679 { yyVAL.indexHints = &IndexHints{Type: ForceStr, Indexes: yyDollar[4].colIdents} } - case 103: + case 107: yyDollar = yyS[yypt-1 : yypt+1] - //line ./go/vt/sqlparser/sql.y:653 + //line ./go/vt/sqlparser/sql.y:685 { yyVAL.colIdents = []ColIdent{yyDollar[1].colIdent} } - case 104: + case 108: yyDollar = yyS[yypt-3 : yypt+1] - //line ./go/vt/sqlparser/sql.y:657 + //line ./go/vt/sqlparser/sql.y:689 { yyVAL.colIdents = append(yyDollar[1].colIdents, yyDollar[3].colIdent) } - case 105: + case 109: yyDollar = yyS[yypt-0 : yypt+1] - //line ./go/vt/sqlparser/sql.y:662 + //line ./go/vt/sqlparser/sql.y:694 { yyVAL.expr = nil } - case 106: + case 110: yyDollar = yyS[yypt-2 : yypt+1] - //line ./go/vt/sqlparser/sql.y:666 + //line ./go/vt/sqlparser/sql.y:698 { yyVAL.expr = yyDollar[2].expr } - case 107: + case 111: yyDollar = yyS[yypt-1 : yypt+1] - //line ./go/vt/sqlparser/sql.y:672 + //line ./go/vt/sqlparser/sql.y:704 { yyVAL.expr = yyDollar[1].expr } - case 108: + case 112: yyDollar = yyS[yypt-3 : yypt+1] - //line ./go/vt/sqlparser/sql.y:676 + //line ./go/vt/sqlparser/sql.y:708 { yyVAL.expr = &AndExpr{Left: yyDollar[1].expr, Right: yyDollar[3].expr} } - case 109: + case 113: yyDollar = yyS[yypt-3 : yypt+1] - //line ./go/vt/sqlparser/sql.y:680 + //line ./go/vt/sqlparser/sql.y:712 { yyVAL.expr = &OrExpr{Left: yyDollar[1].expr, Right: yyDollar[3].expr} } - case 110: + case 114: yyDollar = yyS[yypt-2 : yypt+1] - //line ./go/vt/sqlparser/sql.y:684 + //line ./go/vt/sqlparser/sql.y:716 { yyVAL.expr = &NotExpr{Expr: yyDollar[2].expr} } - case 111: + case 115: yyDollar = yyS[yypt-3 : yypt+1] - //line ./go/vt/sqlparser/sql.y:688 + //line ./go/vt/sqlparser/sql.y:720 { yyVAL.expr = &IsExpr{Operator: yyDollar[3].str, Expr: yyDollar[1].expr} } - case 112: + case 116: yyDollar = yyS[yypt-1 : yypt+1] - //line ./go/vt/sqlparser/sql.y:692 + //line ./go/vt/sqlparser/sql.y:724 { yyVAL.expr = yyDollar[1].expr } - case 113: + case 117: yyDollar = yyS[yypt-1 : yypt+1] - //line ./go/vt/sqlparser/sql.y:698 + //line ./go/vt/sqlparser/sql.y:730 { yyVAL.boolVal = BoolVal(true) } - case 114: + case 118: yyDollar = yyS[yypt-1 : yypt+1] - //line ./go/vt/sqlparser/sql.y:702 + //line ./go/vt/sqlparser/sql.y:734 { yyVAL.boolVal = BoolVal(false) } - case 115: + case 119: yyDollar = yyS[yypt-1 : yypt+1] - //line ./go/vt/sqlparser/sql.y:708 + //line ./go/vt/sqlparser/sql.y:740 { yyVAL.expr = yyDollar[1].boolVal } - case 116: + case 120: yyDollar = yyS[yypt-3 : yypt+1] - //line ./go/vt/sqlparser/sql.y:712 + //line ./go/vt/sqlparser/sql.y:744 { yyVAL.expr = &ComparisonExpr{Left: yyDollar[1].expr, Operator: yyDollar[2].str, Right: yyDollar[3].boolVal} } - case 117: + case 121: yyDollar = yyS[yypt-3 : yypt+1] - //line ./go/vt/sqlparser/sql.y:716 + //line ./go/vt/sqlparser/sql.y:748 { yyVAL.expr = &ComparisonExpr{Left: yyDollar[1].expr, Operator: yyDollar[2].str, Right: yyDollar[3].expr} } - case 118: + case 122: yyDollar = yyS[yypt-3 : yypt+1] - //line ./go/vt/sqlparser/sql.y:720 + //line ./go/vt/sqlparser/sql.y:752 { yyVAL.expr = &ComparisonExpr{Left: yyDollar[1].expr, Operator: InStr, Right: yyDollar[3].colTuple} } - case 119: + case 123: yyDollar = yyS[yypt-4 : yypt+1] - //line ./go/vt/sqlparser/sql.y:724 + //line ./go/vt/sqlparser/sql.y:756 { yyVAL.expr = &ComparisonExpr{Left: yyDollar[1].expr, Operator: NotInStr, Right: yyDollar[4].colTuple} } - case 120: + case 124: yyDollar = yyS[yypt-4 : yypt+1] - //line ./go/vt/sqlparser/sql.y:728 + //line ./go/vt/sqlparser/sql.y:760 { yyVAL.expr = &ComparisonExpr{Left: yyDollar[1].expr, Operator: LikeStr, Right: yyDollar[3].expr, Escape: yyDollar[4].expr} } - case 121: + case 125: yyDollar = yyS[yypt-5 : yypt+1] - //line ./go/vt/sqlparser/sql.y:732 + //line ./go/vt/sqlparser/sql.y:764 { yyVAL.expr = &ComparisonExpr{Left: yyDollar[1].expr, Operator: NotLikeStr, Right: yyDollar[4].expr, Escape: yyDollar[5].expr} } - case 122: + case 126: yyDollar = yyS[yypt-3 : yypt+1] - //line ./go/vt/sqlparser/sql.y:736 + //line ./go/vt/sqlparser/sql.y:768 { yyVAL.expr = &ComparisonExpr{Left: yyDollar[1].expr, Operator: RegexpStr, Right: yyDollar[3].expr} } - case 123: + case 127: yyDollar = yyS[yypt-4 : yypt+1] - //line ./go/vt/sqlparser/sql.y:740 + //line ./go/vt/sqlparser/sql.y:772 { yyVAL.expr = &ComparisonExpr{Left: yyDollar[1].expr, Operator: NotRegexpStr, Right: yyDollar[4].expr} } - case 124: + case 128: yyDollar = yyS[yypt-5 : yypt+1] - //line ./go/vt/sqlparser/sql.y:744 + //line ./go/vt/sqlparser/sql.y:776 { yyVAL.expr = &RangeCond{Left: yyDollar[1].expr, Operator: BetweenStr, From: yyDollar[3].expr, To: yyDollar[5].expr} } - case 125: + case 129: yyDollar = yyS[yypt-6 : yypt+1] - //line ./go/vt/sqlparser/sql.y:748 + //line ./go/vt/sqlparser/sql.y:780 { yyVAL.expr = &RangeCond{Left: yyDollar[1].expr, Operator: NotBetweenStr, From: yyDollar[4].expr, To: yyDollar[6].expr} } - case 126: + case 130: yyDollar = yyS[yypt-2 : yypt+1] - //line ./go/vt/sqlparser/sql.y:752 + //line ./go/vt/sqlparser/sql.y:784 { yyVAL.expr = &ExistsExpr{Subquery: yyDollar[2].subquery} } - case 127: + case 131: yyDollar = yyS[yypt-1 : yypt+1] - //line ./go/vt/sqlparser/sql.y:758 + //line ./go/vt/sqlparser/sql.y:790 { yyVAL.str = IsNullStr } - case 128: + case 132: yyDollar = yyS[yypt-2 : yypt+1] - //line ./go/vt/sqlparser/sql.y:762 + //line ./go/vt/sqlparser/sql.y:794 { yyVAL.str = IsNotNullStr } - case 129: + case 133: yyDollar = yyS[yypt-1 : yypt+1] - //line ./go/vt/sqlparser/sql.y:766 + //line ./go/vt/sqlparser/sql.y:798 { yyVAL.str = IsTrueStr } - case 130: + case 134: yyDollar = yyS[yypt-2 : yypt+1] - //line ./go/vt/sqlparser/sql.y:770 + //line ./go/vt/sqlparser/sql.y:802 { yyVAL.str = IsNotTrueStr } - case 131: + case 135: yyDollar = yyS[yypt-1 : yypt+1] - //line ./go/vt/sqlparser/sql.y:774 + //line ./go/vt/sqlparser/sql.y:806 { yyVAL.str = IsFalseStr } - case 132: + case 136: yyDollar = yyS[yypt-2 : yypt+1] - //line ./go/vt/sqlparser/sql.y:778 + //line ./go/vt/sqlparser/sql.y:810 { yyVAL.str = IsNotFalseStr } - case 133: + case 137: yyDollar = yyS[yypt-1 : yypt+1] - //line ./go/vt/sqlparser/sql.y:784 + //line ./go/vt/sqlparser/sql.y:816 { yyVAL.str = EqualStr } - case 134: + case 138: yyDollar = yyS[yypt-1 : yypt+1] - //line ./go/vt/sqlparser/sql.y:788 + //line ./go/vt/sqlparser/sql.y:820 { yyVAL.str = LessThanStr } - case 135: + case 139: yyDollar = yyS[yypt-1 : yypt+1] - //line ./go/vt/sqlparser/sql.y:792 + //line ./go/vt/sqlparser/sql.y:824 { yyVAL.str = GreaterThanStr } - case 136: + case 140: yyDollar = yyS[yypt-1 : yypt+1] - //line ./go/vt/sqlparser/sql.y:796 + //line ./go/vt/sqlparser/sql.y:828 { yyVAL.str = LessEqualStr } - case 137: + case 141: yyDollar = yyS[yypt-1 : yypt+1] - //line ./go/vt/sqlparser/sql.y:800 + //line ./go/vt/sqlparser/sql.y:832 { yyVAL.str = GreaterEqualStr } - case 138: + case 142: yyDollar = yyS[yypt-1 : yypt+1] - //line ./go/vt/sqlparser/sql.y:804 + //line ./go/vt/sqlparser/sql.y:836 { yyVAL.str = NotEqualStr } - case 139: + case 143: yyDollar = yyS[yypt-1 : yypt+1] - //line ./go/vt/sqlparser/sql.y:808 + //line ./go/vt/sqlparser/sql.y:840 { yyVAL.str = NullSafeEqualStr } - case 140: + case 144: yyDollar = yyS[yypt-0 : yypt+1] - //line ./go/vt/sqlparser/sql.y:813 + //line ./go/vt/sqlparser/sql.y:845 { yyVAL.expr = nil } - case 141: + case 145: yyDollar = yyS[yypt-2 : yypt+1] - //line ./go/vt/sqlparser/sql.y:817 + //line ./go/vt/sqlparser/sql.y:849 { yyVAL.expr = yyDollar[2].expr } - case 142: + case 146: yyDollar = yyS[yypt-1 : yypt+1] - //line ./go/vt/sqlparser/sql.y:823 + //line ./go/vt/sqlparser/sql.y:855 { yyVAL.colTuple = yyDollar[1].valTuple } - case 143: + case 147: yyDollar = yyS[yypt-1 : yypt+1] - //line ./go/vt/sqlparser/sql.y:827 + //line ./go/vt/sqlparser/sql.y:859 { yyVAL.colTuple = yyDollar[1].subquery } - case 144: + case 148: yyDollar = yyS[yypt-1 : yypt+1] - //line ./go/vt/sqlparser/sql.y:831 + //line ./go/vt/sqlparser/sql.y:863 { yyVAL.colTuple = ListArg(yyDollar[1].bytes) } - case 145: + case 149: yyDollar = yyS[yypt-3 : yypt+1] - //line ./go/vt/sqlparser/sql.y:837 + //line ./go/vt/sqlparser/sql.y:869 { yyVAL.subquery = &Subquery{yyDollar[2].selStmt} } - case 146: + case 150: yyDollar = yyS[yypt-1 : yypt+1] - //line ./go/vt/sqlparser/sql.y:843 + //line ./go/vt/sqlparser/sql.y:875 { yyVAL.exprs = Exprs{yyDollar[1].expr} } - case 147: + case 151: yyDollar = yyS[yypt-3 : yypt+1] - //line ./go/vt/sqlparser/sql.y:847 + //line ./go/vt/sqlparser/sql.y:879 { yyVAL.exprs = append(yyDollar[1].exprs, yyDollar[3].expr) } - case 148: + case 152: yyDollar = yyS[yypt-1 : yypt+1] - //line ./go/vt/sqlparser/sql.y:853 + //line ./go/vt/sqlparser/sql.y:885 { yyVAL.str = string(yyDollar[1].bytes) } - case 149: + case 153: yyDollar = yyS[yypt-1 : yypt+1] - //line ./go/vt/sqlparser/sql.y:857 + //line ./go/vt/sqlparser/sql.y:889 { yyVAL.str = string(yyDollar[1].bytes) } - case 150: + case 154: yyDollar = yyS[yypt-1 : yypt+1] - //line ./go/vt/sqlparser/sql.y:861 + //line ./go/vt/sqlparser/sql.y:893 { yyVAL.str = string(yyDollar[1].bytes) } - case 151: + case 155: yyDollar = yyS[yypt-1 : yypt+1] - //line ./go/vt/sqlparser/sql.y:865 + //line ./go/vt/sqlparser/sql.y:897 { yyVAL.str = string(yyDollar[1].bytes) } - case 152: + case 156: yyDollar = yyS[yypt-1 : yypt+1] - //line ./go/vt/sqlparser/sql.y:871 + //line ./go/vt/sqlparser/sql.y:903 { yyVAL.expr = yyDollar[1].expr } - case 153: + case 157: yyDollar = yyS[yypt-1 : yypt+1] - //line ./go/vt/sqlparser/sql.y:875 + //line ./go/vt/sqlparser/sql.y:907 { yyVAL.expr = yyDollar[1].colName } - case 154: + case 158: yyDollar = yyS[yypt-1 : yypt+1] - //line ./go/vt/sqlparser/sql.y:879 + //line ./go/vt/sqlparser/sql.y:911 { yyVAL.expr = yyDollar[1].expr } - case 155: + case 159: yyDollar = yyS[yypt-1 : yypt+1] - //line ./go/vt/sqlparser/sql.y:883 + //line ./go/vt/sqlparser/sql.y:915 { yyVAL.expr = yyDollar[1].subquery } - case 156: + case 160: yyDollar = yyS[yypt-3 : yypt+1] - //line ./go/vt/sqlparser/sql.y:887 + //line ./go/vt/sqlparser/sql.y:919 { yyVAL.expr = &BinaryExpr{Left: yyDollar[1].expr, Operator: BitAndStr, Right: yyDollar[3].expr} } - case 157: + case 161: yyDollar = yyS[yypt-3 : yypt+1] - //line ./go/vt/sqlparser/sql.y:891 + //line ./go/vt/sqlparser/sql.y:923 { yyVAL.expr = &BinaryExpr{Left: yyDollar[1].expr, Operator: BitOrStr, Right: yyDollar[3].expr} } - case 158: + case 162: yyDollar = yyS[yypt-3 : yypt+1] - //line ./go/vt/sqlparser/sql.y:895 + //line ./go/vt/sqlparser/sql.y:927 { yyVAL.expr = &BinaryExpr{Left: yyDollar[1].expr, Operator: BitXorStr, Right: yyDollar[3].expr} } - case 159: + case 163: yyDollar = yyS[yypt-3 : yypt+1] - //line ./go/vt/sqlparser/sql.y:899 + //line ./go/vt/sqlparser/sql.y:931 { yyVAL.expr = &BinaryExpr{Left: yyDollar[1].expr, Operator: PlusStr, Right: yyDollar[3].expr} } - case 160: + case 164: yyDollar = yyS[yypt-3 : yypt+1] - //line ./go/vt/sqlparser/sql.y:903 + //line ./go/vt/sqlparser/sql.y:935 { yyVAL.expr = &BinaryExpr{Left: yyDollar[1].expr, Operator: MinusStr, Right: yyDollar[3].expr} } - case 161: + case 165: yyDollar = yyS[yypt-3 : yypt+1] - //line ./go/vt/sqlparser/sql.y:907 + //line ./go/vt/sqlparser/sql.y:939 { yyVAL.expr = &BinaryExpr{Left: yyDollar[1].expr, Operator: MultStr, Right: yyDollar[3].expr} } - case 162: + case 166: yyDollar = yyS[yypt-3 : yypt+1] - //line ./go/vt/sqlparser/sql.y:911 + //line ./go/vt/sqlparser/sql.y:943 { yyVAL.expr = &BinaryExpr{Left: yyDollar[1].expr, Operator: DivStr, Right: yyDollar[3].expr} } - case 163: + case 167: yyDollar = yyS[yypt-3 : yypt+1] - //line ./go/vt/sqlparser/sql.y:915 + //line ./go/vt/sqlparser/sql.y:947 { yyVAL.expr = &BinaryExpr{Left: yyDollar[1].expr, Operator: IntDivStr, Right: yyDollar[3].expr} } - case 164: + case 168: yyDollar = yyS[yypt-3 : yypt+1] - //line ./go/vt/sqlparser/sql.y:919 + //line ./go/vt/sqlparser/sql.y:951 { yyVAL.expr = &BinaryExpr{Left: yyDollar[1].expr, Operator: ModStr, Right: yyDollar[3].expr} } - case 165: + case 169: yyDollar = yyS[yypt-3 : yypt+1] - //line ./go/vt/sqlparser/sql.y:923 + //line ./go/vt/sqlparser/sql.y:955 { yyVAL.expr = &BinaryExpr{Left: yyDollar[1].expr, Operator: ModStr, Right: yyDollar[3].expr} } - case 166: + case 170: yyDollar = yyS[yypt-3 : yypt+1] - //line ./go/vt/sqlparser/sql.y:927 + //line ./go/vt/sqlparser/sql.y:959 { yyVAL.expr = &BinaryExpr{Left: yyDollar[1].expr, Operator: ShiftLeftStr, Right: yyDollar[3].expr} } - case 167: + case 171: yyDollar = yyS[yypt-3 : yypt+1] - //line ./go/vt/sqlparser/sql.y:931 + //line ./go/vt/sqlparser/sql.y:963 { yyVAL.expr = &BinaryExpr{Left: yyDollar[1].expr, Operator: ShiftRightStr, Right: yyDollar[3].expr} } - case 168: + case 172: yyDollar = yyS[yypt-3 : yypt+1] - //line ./go/vt/sqlparser/sql.y:935 + //line ./go/vt/sqlparser/sql.y:967 { yyVAL.expr = &BinaryExpr{Left: yyDollar[1].colName, Operator: JSONExtractOp, Right: yyDollar[3].expr} } - case 169: + case 173: yyDollar = yyS[yypt-3 : yypt+1] - //line ./go/vt/sqlparser/sql.y:939 + //line ./go/vt/sqlparser/sql.y:971 { yyVAL.expr = &BinaryExpr{Left: yyDollar[1].colName, Operator: JSONUnquoteExtractOp, Right: yyDollar[3].expr} } - case 170: + case 174: yyDollar = yyS[yypt-3 : yypt+1] - //line ./go/vt/sqlparser/sql.y:943 + //line ./go/vt/sqlparser/sql.y:975 { yyVAL.expr = &CollateExpr{Expr: yyDollar[1].expr, Charset: yyDollar[3].str} } - case 171: + case 175: yyDollar = yyS[yypt-2 : yypt+1] - //line ./go/vt/sqlparser/sql.y:947 + //line ./go/vt/sqlparser/sql.y:979 { yyVAL.expr = &UnaryExpr{Operator: BinaryStr, Expr: yyDollar[2].expr} } - case 172: + case 176: yyDollar = yyS[yypt-2 : yypt+1] - //line ./go/vt/sqlparser/sql.y:951 + //line ./go/vt/sqlparser/sql.y:983 { if num, ok := yyDollar[2].expr.(*SQLVal); ok && num.Type == IntVal { yyVAL.expr = num @@ -2358,9 +2430,9 @@ yydefault: yyVAL.expr = &UnaryExpr{Operator: UPlusStr, Expr: yyDollar[2].expr} } } - case 173: + case 177: yyDollar = yyS[yypt-2 : yypt+1] - //line ./go/vt/sqlparser/sql.y:959 + //line ./go/vt/sqlparser/sql.y:991 { if num, ok := yyDollar[2].expr.(*SQLVal); ok && num.Type == IntVal { // Handle double negative @@ -2374,21 +2446,21 @@ yydefault: yyVAL.expr = &UnaryExpr{Operator: UMinusStr, Expr: yyDollar[2].expr} } } - case 174: + case 178: yyDollar = yyS[yypt-2 : yypt+1] - //line ./go/vt/sqlparser/sql.y:973 + //line ./go/vt/sqlparser/sql.y:1005 { yyVAL.expr = &UnaryExpr{Operator: TildaStr, Expr: yyDollar[2].expr} } - case 175: + case 179: yyDollar = yyS[yypt-2 : yypt+1] - //line ./go/vt/sqlparser/sql.y:977 + //line ./go/vt/sqlparser/sql.y:1009 { yyVAL.expr = &UnaryExpr{Operator: BangStr, Expr: yyDollar[2].expr} } - case 176: + case 180: yyDollar = yyS[yypt-3 : yypt+1] - //line ./go/vt/sqlparser/sql.y:981 + //line ./go/vt/sqlparser/sql.y:1013 { // This rule prevents the usage of INTERVAL // as a function. If support is needed for that, @@ -2396,345 +2468,345 @@ yydefault: // will be non-trivial because of grammar conflicts. yyVAL.expr = &IntervalExpr{Expr: yyDollar[2].expr, Unit: yyDollar[3].colIdent} } - case 181: + case 185: yyDollar = yyS[yypt-4 : yypt+1] - //line ./go/vt/sqlparser/sql.y:999 + //line ./go/vt/sqlparser/sql.y:1031 { yyVAL.expr = &FuncExpr{Name: yyDollar[1].colIdent, Exprs: yyDollar[3].selectExprs} } - case 182: + case 186: yyDollar = yyS[yypt-5 : yypt+1] - //line ./go/vt/sqlparser/sql.y:1003 + //line ./go/vt/sqlparser/sql.y:1035 { yyVAL.expr = &FuncExpr{Name: yyDollar[1].colIdent, Distinct: true, Exprs: yyDollar[4].selectExprs} } - case 183: + case 187: yyDollar = yyS[yypt-6 : yypt+1] - //line ./go/vt/sqlparser/sql.y:1007 + //line ./go/vt/sqlparser/sql.y:1039 { yyVAL.expr = &FuncExpr{Qualifier: yyDollar[1].tableIdent, Name: yyDollar[3].colIdent, Exprs: yyDollar[5].selectExprs} } - case 184: + case 188: yyDollar = yyS[yypt-4 : yypt+1] - //line ./go/vt/sqlparser/sql.y:1017 + //line ./go/vt/sqlparser/sql.y:1049 { yyVAL.expr = &FuncExpr{Name: NewColIdent("left"), Exprs: yyDollar[3].selectExprs} } - case 185: + case 189: yyDollar = yyS[yypt-4 : yypt+1] - //line ./go/vt/sqlparser/sql.y:1021 + //line ./go/vt/sqlparser/sql.y:1053 { yyVAL.expr = &FuncExpr{Name: NewColIdent("right"), Exprs: yyDollar[3].selectExprs} } - case 186: + case 190: yyDollar = yyS[yypt-6 : yypt+1] - //line ./go/vt/sqlparser/sql.y:1025 + //line ./go/vt/sqlparser/sql.y:1057 { yyVAL.expr = &ConvertExpr{Expr: yyDollar[3].expr, Type: yyDollar[5].convertType} } - case 187: + case 191: yyDollar = yyS[yypt-6 : yypt+1] - //line ./go/vt/sqlparser/sql.y:1029 + //line ./go/vt/sqlparser/sql.y:1061 { yyVAL.expr = &ConvertExpr{Expr: yyDollar[3].expr, Type: yyDollar[5].convertType} } - case 188: + case 192: yyDollar = yyS[yypt-6 : yypt+1] - //line ./go/vt/sqlparser/sql.y:1033 + //line ./go/vt/sqlparser/sql.y:1065 { yyVAL.expr = &ConvertExpr{Expr: yyDollar[3].expr, Type: yyDollar[5].convertType} } - case 189: + case 193: yyDollar = yyS[yypt-9 : yypt+1] - //line ./go/vt/sqlparser/sql.y:1037 + //line ./go/vt/sqlparser/sql.y:1069 { yyVAL.expr = &MatchExpr{Columns: yyDollar[3].columns, Expr: yyDollar[7].expr, Option: yyDollar[8].str} } - case 190: + case 194: yyDollar = yyS[yypt-7 : yypt+1] - //line ./go/vt/sqlparser/sql.y:1041 + //line ./go/vt/sqlparser/sql.y:1073 { yyVAL.expr = &GroupConcatExpr{Distinct: yyDollar[3].str, Exprs: yyDollar[4].selectExprs, OrderBy: yyDollar[5].orderBy, Separator: yyDollar[6].str} } - case 191: + case 195: yyDollar = yyS[yypt-5 : yypt+1] - //line ./go/vt/sqlparser/sql.y:1045 + //line ./go/vt/sqlparser/sql.y:1077 { yyVAL.expr = &CaseExpr{Expr: yyDollar[2].expr, Whens: yyDollar[3].whens, Else: yyDollar[4].expr} } - case 192: + case 196: yyDollar = yyS[yypt-4 : yypt+1] - //line ./go/vt/sqlparser/sql.y:1049 + //line ./go/vt/sqlparser/sql.y:1081 { yyVAL.expr = &ValuesFuncExpr{Name: yyDollar[3].colIdent} } - case 193: + case 197: yyDollar = yyS[yypt-2 : yypt+1] - //line ./go/vt/sqlparser/sql.y:1059 + //line ./go/vt/sqlparser/sql.y:1091 { yyVAL.expr = &FuncExpr{Name: NewColIdent("current_timestamp")} } - case 194: + case 198: yyDollar = yyS[yypt-2 : yypt+1] - //line ./go/vt/sqlparser/sql.y:1063 + //line ./go/vt/sqlparser/sql.y:1095 { yyVAL.expr = &FuncExpr{Name: NewColIdent("utc_timestamp")} } - case 195: + case 199: yyDollar = yyS[yypt-2 : yypt+1] - //line ./go/vt/sqlparser/sql.y:1067 + //line ./go/vt/sqlparser/sql.y:1099 { yyVAL.expr = &FuncExpr{Name: NewColIdent("utc_time")} } - case 196: + case 200: yyDollar = yyS[yypt-2 : yypt+1] - //line ./go/vt/sqlparser/sql.y:1071 + //line ./go/vt/sqlparser/sql.y:1103 { yyVAL.expr = &FuncExpr{Name: NewColIdent("utc_date")} } - case 197: + case 201: yyDollar = yyS[yypt-2 : yypt+1] - //line ./go/vt/sqlparser/sql.y:1076 + //line ./go/vt/sqlparser/sql.y:1108 { yyVAL.expr = &FuncExpr{Name: NewColIdent("localtime")} } - case 198: + case 202: yyDollar = yyS[yypt-2 : yypt+1] - //line ./go/vt/sqlparser/sql.y:1081 + //line ./go/vt/sqlparser/sql.y:1113 { yyVAL.expr = &FuncExpr{Name: NewColIdent("localtimestamp")} } - case 199: + case 203: yyDollar = yyS[yypt-2 : yypt+1] - //line ./go/vt/sqlparser/sql.y:1086 + //line ./go/vt/sqlparser/sql.y:1118 { yyVAL.expr = &FuncExpr{Name: NewColIdent("current_date")} } - case 200: + case 204: yyDollar = yyS[yypt-2 : yypt+1] - //line ./go/vt/sqlparser/sql.y:1091 + //line ./go/vt/sqlparser/sql.y:1123 { yyVAL.expr = &FuncExpr{Name: NewColIdent("current_time")} } - case 203: + case 207: yyDollar = yyS[yypt-4 : yypt+1] - //line ./go/vt/sqlparser/sql.y:1105 + //line ./go/vt/sqlparser/sql.y:1137 { yyVAL.expr = &FuncExpr{Name: NewColIdent("if"), Exprs: yyDollar[3].selectExprs} } - case 204: + case 208: yyDollar = yyS[yypt-4 : yypt+1] - //line ./go/vt/sqlparser/sql.y:1109 + //line ./go/vt/sqlparser/sql.y:1141 { yyVAL.expr = &FuncExpr{Name: NewColIdent("database"), Exprs: yyDollar[3].selectExprs} } - case 205: + case 209: yyDollar = yyS[yypt-4 : yypt+1] - //line ./go/vt/sqlparser/sql.y:1113 + //line ./go/vt/sqlparser/sql.y:1145 { yyVAL.expr = &FuncExpr{Name: NewColIdent("mod"), Exprs: yyDollar[3].selectExprs} } - case 206: + case 210: yyDollar = yyS[yypt-4 : yypt+1] - //line ./go/vt/sqlparser/sql.y:1117 + //line ./go/vt/sqlparser/sql.y:1149 { yyVAL.expr = &FuncExpr{Name: NewColIdent("replace"), Exprs: yyDollar[3].selectExprs} } - case 207: + case 211: yyDollar = yyS[yypt-0 : yypt+1] - //line ./go/vt/sqlparser/sql.y:1123 + //line ./go/vt/sqlparser/sql.y:1155 { yyVAL.str = "" } - case 208: + case 212: yyDollar = yyS[yypt-3 : yypt+1] - //line ./go/vt/sqlparser/sql.y:1127 + //line ./go/vt/sqlparser/sql.y:1159 { yyVAL.str = BooleanModeStr } - case 209: + case 213: yyDollar = yyS[yypt-4 : yypt+1] - //line ./go/vt/sqlparser/sql.y:1131 + //line ./go/vt/sqlparser/sql.y:1163 { yyVAL.str = NaturalLanguageModeStr } - case 210: + case 214: yyDollar = yyS[yypt-7 : yypt+1] - //line ./go/vt/sqlparser/sql.y:1135 + //line ./go/vt/sqlparser/sql.y:1167 { yyVAL.str = NaturalLanguageModeWithQueryExpansionStr } - case 211: + case 215: yyDollar = yyS[yypt-3 : yypt+1] - //line ./go/vt/sqlparser/sql.y:1139 + //line ./go/vt/sqlparser/sql.y:1171 { yyVAL.str = QueryExpansionStr } - case 212: + case 216: yyDollar = yyS[yypt-1 : yypt+1] - //line ./go/vt/sqlparser/sql.y:1146 + //line ./go/vt/sqlparser/sql.y:1178 { yyVAL.convertType = &ConvertType{Type: yyDollar[1].str} } - case 213: + case 217: yyDollar = yyS[yypt-2 : yypt+1] - //line ./go/vt/sqlparser/sql.y:1150 + //line ./go/vt/sqlparser/sql.y:1182 { yyVAL.convertType = &ConvertType{Type: yyDollar[1].str} } - case 214: + case 218: yyDollar = yyS[yypt-4 : yypt+1] - //line ./go/vt/sqlparser/sql.y:1154 + //line ./go/vt/sqlparser/sql.y:1186 { yyVAL.convertType = &ConvertType{Type: yyDollar[1].str, Length: NewIntVal(yyDollar[3].bytes)} } - case 215: + case 219: yyDollar = yyS[yypt-5 : yypt+1] - //line ./go/vt/sqlparser/sql.y:1158 + //line ./go/vt/sqlparser/sql.y:1190 { yyVAL.convertType = &ConvertType{Type: yyDollar[1].str, Length: NewIntVal(yyDollar[3].bytes), Charset: yyDollar[5].str} } - case 216: + case 220: yyDollar = yyS[yypt-7 : yypt+1] - //line ./go/vt/sqlparser/sql.y:1162 + //line ./go/vt/sqlparser/sql.y:1194 { yyVAL.convertType = &ConvertType{Type: yyDollar[1].str, Length: NewIntVal(yyDollar[3].bytes), Charset: yyDollar[7].str, Operator: CharacterSetStr} } - case 217: + case 221: yyDollar = yyS[yypt-2 : yypt+1] - //line ./go/vt/sqlparser/sql.y:1166 + //line ./go/vt/sqlparser/sql.y:1198 { yyVAL.convertType = &ConvertType{Type: yyDollar[1].str, Charset: yyDollar[2].str} } - case 218: + case 222: yyDollar = yyS[yypt-4 : yypt+1] - //line ./go/vt/sqlparser/sql.y:1170 + //line ./go/vt/sqlparser/sql.y:1202 { yyVAL.convertType = &ConvertType{Type: yyDollar[1].str, Charset: yyDollar[4].str, Operator: CharacterSetStr} } - case 219: + case 223: yyDollar = yyS[yypt-6 : yypt+1] - //line ./go/vt/sqlparser/sql.y:1174 + //line ./go/vt/sqlparser/sql.y:1206 { yyVAL.convertType = &ConvertType{Type: yyDollar[1].str, Length: NewIntVal(yyDollar[3].bytes), Scale: NewIntVal(yyDollar[5].bytes)} } - case 220: + case 224: yyDollar = yyS[yypt-0 : yypt+1] - //line ./go/vt/sqlparser/sql.y:1179 + //line ./go/vt/sqlparser/sql.y:1211 { yyVAL.expr = nil } - case 221: + case 225: yyDollar = yyS[yypt-1 : yypt+1] - //line ./go/vt/sqlparser/sql.y:1183 + //line ./go/vt/sqlparser/sql.y:1215 { yyVAL.expr = yyDollar[1].expr } - case 222: + case 226: yyDollar = yyS[yypt-0 : yypt+1] - //line ./go/vt/sqlparser/sql.y:1188 + //line ./go/vt/sqlparser/sql.y:1220 { yyVAL.str = string("") } - case 223: + case 227: yyDollar = yyS[yypt-2 : yypt+1] - //line ./go/vt/sqlparser/sql.y:1192 + //line ./go/vt/sqlparser/sql.y:1224 { yyVAL.str = " separator '" + string(yyDollar[2].bytes) + "'" } - case 224: + case 228: yyDollar = yyS[yypt-1 : yypt+1] - //line ./go/vt/sqlparser/sql.y:1198 + //line ./go/vt/sqlparser/sql.y:1230 { yyVAL.whens = []*When{yyDollar[1].when} } - case 225: + case 229: yyDollar = yyS[yypt-2 : yypt+1] - //line ./go/vt/sqlparser/sql.y:1202 + //line ./go/vt/sqlparser/sql.y:1234 { yyVAL.whens = append(yyDollar[1].whens, yyDollar[2].when) } - case 226: + case 230: yyDollar = yyS[yypt-4 : yypt+1] - //line ./go/vt/sqlparser/sql.y:1208 + //line ./go/vt/sqlparser/sql.y:1240 { yyVAL.when = &When{Cond: yyDollar[2].expr, Val: yyDollar[4].expr} } - case 227: + case 231: yyDollar = yyS[yypt-0 : yypt+1] - //line ./go/vt/sqlparser/sql.y:1213 + //line ./go/vt/sqlparser/sql.y:1245 { yyVAL.expr = nil } - case 228: + case 232: yyDollar = yyS[yypt-2 : yypt+1] - //line ./go/vt/sqlparser/sql.y:1217 + //line ./go/vt/sqlparser/sql.y:1249 { yyVAL.expr = yyDollar[2].expr } - case 229: + case 233: yyDollar = yyS[yypt-1 : yypt+1] - //line ./go/vt/sqlparser/sql.y:1223 + //line ./go/vt/sqlparser/sql.y:1255 { yyVAL.colName = &ColName{Name: yyDollar[1].colIdent} } - case 230: + case 234: yyDollar = yyS[yypt-3 : yypt+1] - //line ./go/vt/sqlparser/sql.y:1227 + //line ./go/vt/sqlparser/sql.y:1259 { yyVAL.colName = &ColName{Qualifier: &TableName{Name: yyDollar[1].tableIdent}, Name: yyDollar[3].colIdent} } - case 231: + case 235: yyDollar = yyS[yypt-5 : yypt+1] - //line ./go/vt/sqlparser/sql.y:1231 + //line ./go/vt/sqlparser/sql.y:1263 { yyVAL.colName = &ColName{Qualifier: &TableName{Qualifier: yyDollar[1].tableIdent, Name: yyDollar[3].tableIdent}, Name: yyDollar[5].colIdent} } - case 232: + case 236: yyDollar = yyS[yypt-1 : yypt+1] - //line ./go/vt/sqlparser/sql.y:1237 + //line ./go/vt/sqlparser/sql.y:1269 { yyVAL.expr = NewStrVal(yyDollar[1].bytes) } - case 233: + case 237: yyDollar = yyS[yypt-1 : yypt+1] - //line ./go/vt/sqlparser/sql.y:1241 + //line ./go/vt/sqlparser/sql.y:1273 { yyVAL.expr = NewHexVal(yyDollar[1].bytes) } - case 234: + case 238: yyDollar = yyS[yypt-1 : yypt+1] - //line ./go/vt/sqlparser/sql.y:1245 + //line ./go/vt/sqlparser/sql.y:1277 { yyVAL.expr = NewIntVal(yyDollar[1].bytes) } - case 235: + case 239: yyDollar = yyS[yypt-1 : yypt+1] - //line ./go/vt/sqlparser/sql.y:1249 + //line ./go/vt/sqlparser/sql.y:1281 { yyVAL.expr = NewFloatVal(yyDollar[1].bytes) } - case 236: + case 240: yyDollar = yyS[yypt-1 : yypt+1] - //line ./go/vt/sqlparser/sql.y:1253 + //line ./go/vt/sqlparser/sql.y:1285 { yyVAL.expr = NewHexNum(yyDollar[1].bytes) } - case 237: + case 241: yyDollar = yyS[yypt-1 : yypt+1] - //line ./go/vt/sqlparser/sql.y:1257 + //line ./go/vt/sqlparser/sql.y:1289 { yyVAL.expr = NewValArg(yyDollar[1].bytes) } - case 238: + case 242: yyDollar = yyS[yypt-1 : yypt+1] - //line ./go/vt/sqlparser/sql.y:1261 + //line ./go/vt/sqlparser/sql.y:1293 { yyVAL.expr = &NullVal{} } - case 239: + case 243: yyDollar = yyS[yypt-1 : yypt+1] - //line ./go/vt/sqlparser/sql.y:1267 + //line ./go/vt/sqlparser/sql.y:1299 { // TODO(sougou): Deprecate this construct. if yyDollar[1].colIdent.Lowered() != "value" { @@ -2743,237 +2815,237 @@ yydefault: } yyVAL.expr = NewIntVal([]byte("1")) } - case 240: + case 244: yyDollar = yyS[yypt-2 : yypt+1] - //line ./go/vt/sqlparser/sql.y:1276 + //line ./go/vt/sqlparser/sql.y:1308 { yyVAL.expr = NewIntVal(yyDollar[1].bytes) } - case 241: + case 245: yyDollar = yyS[yypt-2 : yypt+1] - //line ./go/vt/sqlparser/sql.y:1280 + //line ./go/vt/sqlparser/sql.y:1312 { yyVAL.expr = NewValArg(yyDollar[1].bytes) } - case 242: + case 246: yyDollar = yyS[yypt-0 : yypt+1] - //line ./go/vt/sqlparser/sql.y:1285 + //line ./go/vt/sqlparser/sql.y:1317 { yyVAL.exprs = nil } - case 243: + case 247: yyDollar = yyS[yypt-3 : yypt+1] - //line ./go/vt/sqlparser/sql.y:1289 + //line ./go/vt/sqlparser/sql.y:1321 { yyVAL.exprs = yyDollar[3].exprs } - case 244: + case 248: yyDollar = yyS[yypt-0 : yypt+1] - //line ./go/vt/sqlparser/sql.y:1294 + //line ./go/vt/sqlparser/sql.y:1326 { yyVAL.expr = nil } - case 245: + case 249: yyDollar = yyS[yypt-2 : yypt+1] - //line ./go/vt/sqlparser/sql.y:1298 + //line ./go/vt/sqlparser/sql.y:1330 { yyVAL.expr = yyDollar[2].expr } - case 246: + case 250: yyDollar = yyS[yypt-0 : yypt+1] - //line ./go/vt/sqlparser/sql.y:1303 + //line ./go/vt/sqlparser/sql.y:1335 { yyVAL.orderBy = nil } - case 247: + case 251: yyDollar = yyS[yypt-3 : yypt+1] - //line ./go/vt/sqlparser/sql.y:1307 + //line ./go/vt/sqlparser/sql.y:1339 { yyVAL.orderBy = yyDollar[3].orderBy } - case 248: + case 252: yyDollar = yyS[yypt-1 : yypt+1] - //line ./go/vt/sqlparser/sql.y:1313 + //line ./go/vt/sqlparser/sql.y:1345 { yyVAL.orderBy = OrderBy{yyDollar[1].order} } - case 249: + case 253: yyDollar = yyS[yypt-3 : yypt+1] - //line ./go/vt/sqlparser/sql.y:1317 + //line ./go/vt/sqlparser/sql.y:1349 { yyVAL.orderBy = append(yyDollar[1].orderBy, yyDollar[3].order) } - case 250: + case 254: yyDollar = yyS[yypt-2 : yypt+1] - //line ./go/vt/sqlparser/sql.y:1323 + //line ./go/vt/sqlparser/sql.y:1355 { yyVAL.order = &Order{Expr: yyDollar[1].expr, Direction: yyDollar[2].str} } - case 251: + case 255: yyDollar = yyS[yypt-0 : yypt+1] - //line ./go/vt/sqlparser/sql.y:1328 + //line ./go/vt/sqlparser/sql.y:1360 { yyVAL.str = AscScr } - case 252: + case 256: yyDollar = yyS[yypt-1 : yypt+1] - //line ./go/vt/sqlparser/sql.y:1332 + //line ./go/vt/sqlparser/sql.y:1364 { yyVAL.str = AscScr } - case 253: + case 257: yyDollar = yyS[yypt-1 : yypt+1] - //line ./go/vt/sqlparser/sql.y:1336 + //line ./go/vt/sqlparser/sql.y:1368 { yyVAL.str = DescScr } - case 254: + case 258: yyDollar = yyS[yypt-0 : yypt+1] - //line ./go/vt/sqlparser/sql.y:1341 + //line ./go/vt/sqlparser/sql.y:1373 { yyVAL.limit = nil } - case 255: + case 259: yyDollar = yyS[yypt-2 : yypt+1] - //line ./go/vt/sqlparser/sql.y:1345 + //line ./go/vt/sqlparser/sql.y:1377 { yyVAL.limit = &Limit{Rowcount: yyDollar[2].expr} } - case 256: + case 260: yyDollar = yyS[yypt-4 : yypt+1] - //line ./go/vt/sqlparser/sql.y:1349 + //line ./go/vt/sqlparser/sql.y:1381 { yyVAL.limit = &Limit{Offset: yyDollar[2].expr, Rowcount: yyDollar[4].expr} } - case 257: + case 261: yyDollar = yyS[yypt-4 : yypt+1] - //line ./go/vt/sqlparser/sql.y:1353 + //line ./go/vt/sqlparser/sql.y:1385 { yyVAL.limit = &Limit{Offset: yyDollar[4].expr, Rowcount: yyDollar[2].expr} } - case 258: + case 262: yyDollar = yyS[yypt-0 : yypt+1] - //line ./go/vt/sqlparser/sql.y:1358 + //line ./go/vt/sqlparser/sql.y:1390 { yyVAL.str = "" } - case 259: + case 263: yyDollar = yyS[yypt-2 : yypt+1] - //line ./go/vt/sqlparser/sql.y:1362 + //line ./go/vt/sqlparser/sql.y:1394 { yyVAL.str = ForUpdateStr } - case 260: + case 264: yyDollar = yyS[yypt-4 : yypt+1] - //line ./go/vt/sqlparser/sql.y:1366 + //line ./go/vt/sqlparser/sql.y:1398 { yyVAL.str = ShareModeStr } - case 261: + case 265: yyDollar = yyS[yypt-1 : yypt+1] - //line ./go/vt/sqlparser/sql.y:1372 + //line ./go/vt/sqlparser/sql.y:1404 { yyVAL.columns = Columns{yyDollar[1].colIdent} } - case 262: + case 266: yyDollar = yyS[yypt-3 : yypt+1] - //line ./go/vt/sqlparser/sql.y:1376 + //line ./go/vt/sqlparser/sql.y:1408 { yyVAL.columns = append(yyVAL.columns, yyDollar[3].colIdent) } - case 263: + case 267: yyDollar = yyS[yypt-0 : yypt+1] - //line ./go/vt/sqlparser/sql.y:1381 + //line ./go/vt/sqlparser/sql.y:1413 { yyVAL.columns = nil } - case 264: + case 268: yyDollar = yyS[yypt-3 : yypt+1] - //line ./go/vt/sqlparser/sql.y:1385 + //line ./go/vt/sqlparser/sql.y:1417 { yyVAL.columns = yyDollar[2].columns } - case 265: + case 269: yyDollar = yyS[yypt-1 : yypt+1] - //line ./go/vt/sqlparser/sql.y:1391 + //line ./go/vt/sqlparser/sql.y:1423 { yyVAL.columns = Columns{yyDollar[1].colIdent} } - case 266: + case 270: yyDollar = yyS[yypt-3 : yypt+1] - //line ./go/vt/sqlparser/sql.y:1395 + //line ./go/vt/sqlparser/sql.y:1427 { yyVAL.columns = Columns{yyDollar[3].colIdent} } - case 267: + case 271: yyDollar = yyS[yypt-3 : yypt+1] - //line ./go/vt/sqlparser/sql.y:1399 + //line ./go/vt/sqlparser/sql.y:1431 { yyVAL.columns = append(yyVAL.columns, yyDollar[3].colIdent) } - case 268: + case 272: yyDollar = yyS[yypt-5 : yypt+1] - //line ./go/vt/sqlparser/sql.y:1403 + //line ./go/vt/sqlparser/sql.y:1435 { yyVAL.columns = append(yyVAL.columns, yyDollar[5].colIdent) } - case 269: + case 273: yyDollar = yyS[yypt-0 : yypt+1] - //line ./go/vt/sqlparser/sql.y:1408 + //line ./go/vt/sqlparser/sql.y:1440 { yyVAL.updateExprs = nil } - case 270: + case 274: yyDollar = yyS[yypt-5 : yypt+1] - //line ./go/vt/sqlparser/sql.y:1412 + //line ./go/vt/sqlparser/sql.y:1444 { yyVAL.updateExprs = yyDollar[5].updateExprs } - case 271: + case 275: yyDollar = yyS[yypt-2 : yypt+1] - //line ./go/vt/sqlparser/sql.y:1418 + //line ./go/vt/sqlparser/sql.y:1450 { yyVAL.insRows = yyDollar[2].values } - case 272: + case 276: yyDollar = yyS[yypt-1 : yypt+1] - //line ./go/vt/sqlparser/sql.y:1422 + //line ./go/vt/sqlparser/sql.y:1454 { yyVAL.insRows = yyDollar[1].selStmt } - case 273: + case 277: yyDollar = yyS[yypt-1 : yypt+1] - //line ./go/vt/sqlparser/sql.y:1428 + //line ./go/vt/sqlparser/sql.y:1460 { yyVAL.values = Values{yyDollar[1].valTuple} } - case 274: + case 278: yyDollar = yyS[yypt-3 : yypt+1] - //line ./go/vt/sqlparser/sql.y:1432 + //line ./go/vt/sqlparser/sql.y:1464 { yyVAL.values = append(yyDollar[1].values, yyDollar[3].valTuple) } - case 275: + case 279: yyDollar = yyS[yypt-1 : yypt+1] - //line ./go/vt/sqlparser/sql.y:1438 + //line ./go/vt/sqlparser/sql.y:1470 { yyVAL.valTuple = yyDollar[1].valTuple } - case 276: + case 280: yyDollar = yyS[yypt-2 : yypt+1] - //line ./go/vt/sqlparser/sql.y:1442 + //line ./go/vt/sqlparser/sql.y:1474 { yyVAL.valTuple = ValTuple{} } - case 277: + case 281: yyDollar = yyS[yypt-3 : yypt+1] - //line ./go/vt/sqlparser/sql.y:1448 + //line ./go/vt/sqlparser/sql.y:1480 { yyVAL.valTuple = ValTuple(yyDollar[2].exprs) } - case 278: + case 282: yyDollar = yyS[yypt-1 : yypt+1] - //line ./go/vt/sqlparser/sql.y:1454 + //line ./go/vt/sqlparser/sql.y:1486 { if len(yyDollar[1].valTuple) == 1 { yyVAL.expr = &ParenExpr{yyDollar[1].valTuple[0]} @@ -2981,204 +3053,210 @@ yydefault: yyVAL.expr = yyDollar[1].valTuple } } - case 279: + case 283: yyDollar = yyS[yypt-1 : yypt+1] - //line ./go/vt/sqlparser/sql.y:1464 + //line ./go/vt/sqlparser/sql.y:1496 { yyVAL.updateExprs = UpdateExprs{yyDollar[1].updateExpr} } - case 280: + case 284: yyDollar = yyS[yypt-3 : yypt+1] - //line ./go/vt/sqlparser/sql.y:1468 + //line ./go/vt/sqlparser/sql.y:1500 { yyVAL.updateExprs = append(yyDollar[1].updateExprs, yyDollar[3].updateExpr) } - case 281: + case 285: yyDollar = yyS[yypt-3 : yypt+1] - //line ./go/vt/sqlparser/sql.y:1474 + //line ./go/vt/sqlparser/sql.y:1506 { yyVAL.updateExpr = &UpdateExpr{Name: yyDollar[1].colName, Expr: yyDollar[3].expr} } - case 284: + case 288: yyDollar = yyS[yypt-0 : yypt+1] - //line ./go/vt/sqlparser/sql.y:1483 + //line ./go/vt/sqlparser/sql.y:1515 { yyVAL.byt = 0 } - case 285: + case 289: yyDollar = yyS[yypt-2 : yypt+1] - //line ./go/vt/sqlparser/sql.y:1485 + //line ./go/vt/sqlparser/sql.y:1517 { yyVAL.byt = 1 } - case 286: + case 290: yyDollar = yyS[yypt-0 : yypt+1] - //line ./go/vt/sqlparser/sql.y:1488 + //line ./go/vt/sqlparser/sql.y:1520 { yyVAL.empty = struct{}{} } - case 287: + case 291: yyDollar = yyS[yypt-3 : yypt+1] - //line ./go/vt/sqlparser/sql.y:1490 + //line ./go/vt/sqlparser/sql.y:1522 { yyVAL.empty = struct{}{} } - case 288: + case 292: yyDollar = yyS[yypt-0 : yypt+1] - //line ./go/vt/sqlparser/sql.y:1493 + //line ./go/vt/sqlparser/sql.y:1525 { yyVAL.str = "" } - case 289: + case 293: yyDollar = yyS[yypt-1 : yypt+1] - //line ./go/vt/sqlparser/sql.y:1495 + //line ./go/vt/sqlparser/sql.y:1527 { yyVAL.str = IgnoreStr } - case 290: + case 294: yyDollar = yyS[yypt-1 : yypt+1] - //line ./go/vt/sqlparser/sql.y:1499 + //line ./go/vt/sqlparser/sql.y:1531 { yyVAL.empty = struct{}{} } - case 291: + case 295: yyDollar = yyS[yypt-1 : yypt+1] - //line ./go/vt/sqlparser/sql.y:1501 + //line ./go/vt/sqlparser/sql.y:1533 { yyVAL.empty = struct{}{} } - case 292: + case 296: yyDollar = yyS[yypt-1 : yypt+1] - //line ./go/vt/sqlparser/sql.y:1503 + //line ./go/vt/sqlparser/sql.y:1535 { yyVAL.empty = struct{}{} } - case 293: + case 297: yyDollar = yyS[yypt-1 : yypt+1] - //line ./go/vt/sqlparser/sql.y:1505 + //line ./go/vt/sqlparser/sql.y:1537 { yyVAL.empty = struct{}{} } - case 294: + case 298: yyDollar = yyS[yypt-1 : yypt+1] - //line ./go/vt/sqlparser/sql.y:1507 + //line ./go/vt/sqlparser/sql.y:1539 { yyVAL.empty = struct{}{} } - case 295: + case 299: yyDollar = yyS[yypt-1 : yypt+1] - //line ./go/vt/sqlparser/sql.y:1509 + //line ./go/vt/sqlparser/sql.y:1541 { yyVAL.empty = struct{}{} } - case 296: + case 300: yyDollar = yyS[yypt-0 : yypt+1] - //line ./go/vt/sqlparser/sql.y:1512 + //line ./go/vt/sqlparser/sql.y:1544 { yyVAL.empty = struct{}{} } - case 297: + case 301: yyDollar = yyS[yypt-1 : yypt+1] - //line ./go/vt/sqlparser/sql.y:1514 + //line ./go/vt/sqlparser/sql.y:1546 { yyVAL.empty = struct{}{} } - case 298: + case 302: yyDollar = yyS[yypt-0 : yypt+1] - //line ./go/vt/sqlparser/sql.y:1517 + //line ./go/vt/sqlparser/sql.y:1549 { yyVAL.empty = struct{}{} } - case 299: + case 303: yyDollar = yyS[yypt-1 : yypt+1] - //line ./go/vt/sqlparser/sql.y:1519 + //line ./go/vt/sqlparser/sql.y:1551 { yyVAL.empty = struct{}{} } - case 300: + case 304: + yyDollar = yyS[yypt-1 : yypt+1] + //line ./go/vt/sqlparser/sql.y:1553 + { + yyVAL.empty = struct{}{} + } + case 305: yyDollar = yyS[yypt-0 : yypt+1] - //line ./go/vt/sqlparser/sql.y:1522 + //line ./go/vt/sqlparser/sql.y:1556 { yyVAL.empty = struct{}{} } - case 301: + case 306: yyDollar = yyS[yypt-2 : yypt+1] - //line ./go/vt/sqlparser/sql.y:1524 + //line ./go/vt/sqlparser/sql.y:1558 { yyVAL.empty = struct{}{} } - case 302: + case 307: yyDollar = yyS[yypt-1 : yypt+1] - //line ./go/vt/sqlparser/sql.y:1528 + //line ./go/vt/sqlparser/sql.y:1562 { yyVAL.colIdent = NewColIdent(string(yyDollar[1].bytes)) } - case 303: + case 308: yyDollar = yyS[yypt-1 : yypt+1] - //line ./go/vt/sqlparser/sql.y:1532 + //line ./go/vt/sqlparser/sql.y:1566 { yyVAL.colIdent = NewColIdent(string(yyDollar[1].bytes)) } - case 305: + case 310: yyDollar = yyS[yypt-1 : yypt+1] - //line ./go/vt/sqlparser/sql.y:1539 + //line ./go/vt/sqlparser/sql.y:1573 { yyVAL.colIdent = NewColIdent(string(yyDollar[1].bytes)) } - case 306: + case 311: yyDollar = yyS[yypt-1 : yypt+1] - //line ./go/vt/sqlparser/sql.y:1545 + //line ./go/vt/sqlparser/sql.y:1579 { yyVAL.tableIdent = NewTableIdent(string(yyDollar[1].bytes)) } - case 307: + case 312: yyDollar = yyS[yypt-1 : yypt+1] - //line ./go/vt/sqlparser/sql.y:1549 + //line ./go/vt/sqlparser/sql.y:1583 { yyVAL.tableIdent = NewTableIdent(string(yyDollar[1].bytes)) } - case 309: + case 314: yyDollar = yyS[yypt-1 : yypt+1] - //line ./go/vt/sqlparser/sql.y:1556 + //line ./go/vt/sqlparser/sql.y:1590 { yyVAL.tableIdent = NewTableIdent(string(yyDollar[1].bytes)) } - case 407: + case 417: yyDollar = yyS[yypt-1 : yypt+1] - //line ./go/vt/sqlparser/sql.y:1678 + //line ./go/vt/sqlparser/sql.y:1717 { if incNesting(yylex) { yylex.Error("max nesting level reached") return 1 } } - case 408: + case 418: yyDollar = yyS[yypt-1 : yypt+1] - //line ./go/vt/sqlparser/sql.y:1687 + //line ./go/vt/sqlparser/sql.y:1726 { decNesting(yylex) } - case 409: + case 419: yyDollar = yyS[yypt-0 : yypt+1] - //line ./go/vt/sqlparser/sql.y:1692 + //line ./go/vt/sqlparser/sql.y:1731 { forceEOF(yylex) } - case 410: + case 420: yyDollar = yyS[yypt-0 : yypt+1] - //line ./go/vt/sqlparser/sql.y:1697 + //line ./go/vt/sqlparser/sql.y:1736 { forceEOF(yylex) } - case 411: + case 421: yyDollar = yyS[yypt-1 : yypt+1] - //line ./go/vt/sqlparser/sql.y:1701 + //line ./go/vt/sqlparser/sql.y:1740 { forceEOF(yylex) } - case 412: + case 422: yyDollar = yyS[yypt-1 : yypt+1] - //line ./go/vt/sqlparser/sql.y:1705 + //line ./go/vt/sqlparser/sql.y:1744 { forceEOF(yylex) } diff --git a/go/vt/sqlparser/sql.y b/go/vt/sqlparser/sql.y index 1e60726055a..8b9e36ecd6b 100644 --- a/go/vt/sqlparser/sql.y +++ b/go/vt/sqlparser/sql.y @@ -113,6 +113,9 @@ func forceEOF(yylex interface{}) { %token TABLE INDEX VIEW TO IGNORE IF UNIQUE USING %token SHOW DESCRIBE EXPLAIN DATE ESCAPE +// Supported SHOW tokens +%token DATABASES TABLES VITESS_KEYSPACES VITESS_SHARDS VSCHEMA_TABLES + // Convert Type Tokens %token INTEGER CHARACTER @@ -134,7 +137,7 @@ func forceEOF(yylex interface{}) { %type select_statement %type insert_statement update_statement delete_statement set_statement %type create_statement alter_statement rename_statement drop_statement -%type analyze_statement other_statement +%type analyze_statement show_statement other_statement %type comment_opt comment_list %type union_op %type distinct_opt straight_join_opt cache_opt match_option separator_opt @@ -189,7 +192,7 @@ func forceEOF(yylex interface{}) { %type force_eof ddl_force_eof %type charset %type convert_type - +%type show_statement_type %start any_command %% @@ -218,6 +221,7 @@ command: | rename_statement | drop_statement | analyze_statement +| show_statement | other_statement select_statement: @@ -333,12 +337,40 @@ analyze_statement: $$ = &DDL{Action: AlterStr, Table: $3, NewName: $3} } -other_statement: - SHOW force_eof +show_statement_type: + ID { - $$ = &Other{} + $$ = ShowUnsupportedStr } -| DESCRIBE force_eof +| reserved_keyword + { + if (string($1) == "databases"){ + $$ = ShowDatabasesStr + } else if (string($1) == "tables"){ + $$ = ShowTablesStr + } else if (string($1) == "vitess_keyspaces"){ + $$ = ShowKeyspacesStr + } else if (string($1) == "vitess_shards"){ + $$ = ShowShardsStr + } else if (string($1) == "vschema_tables"){ + $$ = ShowVSchemaTablesStr + } else { + $$ = ShowUnsupportedStr + } + } +| non_reserved_keyword +{ + $$ = ShowUnsupportedStr +} + +show_statement: +SHOW show_statement_type force_eof +{ + $$ = &Show{Type: $2} +} + +other_statement: + DESCRIBE force_eof { $$ = &Other{} } @@ -1517,6 +1549,8 @@ constraint_opt: { $$ = struct{}{} } | UNIQUE { $$ = struct{}{} } +| sql_id + { $$ = struct{}{} } using_opt: { $$ = struct{}{} } @@ -1581,6 +1615,7 @@ reserved_keyword: | CURRENT_TIME | CURRENT_TIMESTAMP | DATABASE +| DATABASES | DEFAULT | DELETE | DESC @@ -1633,9 +1668,13 @@ reserved_keyword: | SELECT | SEPARATOR | SET +| VITESS_KEYSPACES +| VITESS_SHARDS +| VSCHEMA_TABLES | SHOW | STRAIGHT_JOIN | TABLE +| TABLES | THEN | TO | TRUE diff --git a/go/vt/sqlparser/token.go b/go/vt/sqlparser/token.go index 230d8ea907f..f5310cfc09f 100644 --- a/go/vt/sqlparser/token.go +++ b/go/vt/sqlparser/token.go @@ -85,7 +85,7 @@ var keywords = map[string]int{ "current_user": UNUSED, "cursor": UNUSED, "database": DATABASE, - "databases": UNUSED, + "databases": DATABASES, "day_hour": UNUSED, "day_microsecond": UNUSED, "day_minute": UNUSED, @@ -258,6 +258,7 @@ var keywords = map[string]int{ "stored": UNUSED, "straight_join": STRAIGHT_JOIN, "table": TABLE, + "tables": TABLES, "terminated": UNUSED, "then": THEN, "tinyblob": UNUSED, @@ -285,6 +286,9 @@ var keywords = map[string]int{ "varying": UNUSED, "virtual": UNUSED, "view": VIEW, + "vitess_keyspaces": VITESS_KEYSPACES, + "vitess_shards": VITESS_SHARDS, + "vschema_tables": VSCHEMA_TABLES, "when": WHEN, "where": WHERE, "while": UNUSED, diff --git a/go/vt/topo/consultopo/replication_graph.go b/go/vt/topo/consultopo/replication_graph.go index 4a22ce3e2d5..2dbccf7ee70 100644 --- a/go/vt/topo/consultopo/replication_graph.go +++ b/go/vt/topo/consultopo/replication_graph.go @@ -49,16 +49,21 @@ func (s *Server) UpdateShardReplicationFields(ctx context.Context, cell, keyspac if version == nil { // We have to create, and we catch ErrNodeExists. _, err = s.Create(ctx, cell, p, data) - if err != topo.ErrNodeExists { - return err - } - } else { - // We have to update, and we catch ErrBadVersion. - _, err = s.Update(ctx, cell, p, data, version) - if err != topo.ErrBadVersion { - return err + if err == topo.ErrNodeExists { + // Node was created by another process, try + // again. + continue } + return err + } + + // We have to update, and we catch ErrBadVersion. + _, err = s.Update(ctx, cell, p, data, version) + if err == topo.ErrBadVersion { + // Node was updated by another process, try again. + continue } + return err } } diff --git a/go/vt/topo/etcd2topo/replication_graph.go b/go/vt/topo/etcd2topo/replication_graph.go index 0262c997515..7feecc8531f 100644 --- a/go/vt/topo/etcd2topo/replication_graph.go +++ b/go/vt/topo/etcd2topo/replication_graph.go @@ -53,16 +53,21 @@ func (s *Server) UpdateShardReplicationFields(ctx context.Context, cell, keyspac if version == nil { // We have to create, and we catch ErrNodeExists. _, err = s.Create(ctx, cell, p, data) - if err != topo.ErrNodeExists { - return err - } - } else { - // We have to update, and we catch ErrBadVersion. - _, err = s.Update(ctx, cell, p, data, version) - if err != topo.ErrBadVersion { - return err + if err == topo.ErrNodeExists { + // Node was created by another process, try + // again. + continue } + return err + } + + // We have to update, and we catch ErrBadVersion. + _, err = s.Update(ctx, cell, p, data, version) + if err == topo.ErrBadVersion { + // Node was updated by another process, try again. + continue } + return err } } diff --git a/go/vt/topo/topoproto/shard.go b/go/vt/topo/topoproto/shard.go index 282b9ec02c3..00d5115c23e 100644 --- a/go/vt/topo/topoproto/shard.go +++ b/go/vt/topo/topoproto/shard.go @@ -30,6 +30,17 @@ func ParseKeyspaceShard(param string) (string, string, error) { return keySpaceShard[0], keySpaceShard[1], nil } +// ParseKeyspaceOptionalShard parses a "keyspace/shard" string +// and extracts the parts. If a shard is not specified, it's +// returned as empty string. +func ParseKeyspaceOptionalShard(keyspaceShard string) (string, string) { + last := strings.LastIndex(keyspaceShard, "/") + if last == -1 { + return keyspaceShard, "" + } + return keyspaceShard[:last], keyspaceShard[last+1:] +} + // SourceShardString returns a printable view of a SourceShard. func SourceShardString(source *topodatapb.Shard_SourceShard) string { return fmt.Sprintf("SourceShard(%v,%v/%v)", source.Uid, source.Keyspace, source.Shard) diff --git a/go/vt/topo/topoproto/shard_test.go b/go/vt/topo/topoproto/shard_test.go index ea8eb3313f4..22bc0d73810 100644 --- a/go/vt/topo/topoproto/shard_test.go +++ b/go/vt/topo/topoproto/shard_test.go @@ -31,6 +31,36 @@ func TestParseKeyspaceShard(t *testing.T) { } } +func TestParseKeyspaceOptionalShard(t *testing.T) { + testcases := []struct { + keyspaceShard string + keyspace string + shard string + }{{ + keyspaceShard: "ks", + keyspace: "ks", + shard: "", + }, { + keyspaceShard: "/-80", + keyspace: "", + shard: "-80", + }, { + keyspaceShard: "ks/-80", + keyspace: "ks", + shard: "-80", + }, { + keyspaceShard: "ks/", + keyspace: "ks", + shard: "", + }} + + for _, tcase := range testcases { + if keyspace, shard := ParseKeyspaceOptionalShard(tcase.keyspaceShard); keyspace != tcase.keyspace || shard != tcase.shard { + t.Errorf("parseKeyspaceShard(%s): %s:%s, want %s:%s", tcase.keyspaceShard, keyspace, shard, tcase.keyspace, tcase.shard) + } + } +} + func TestSourceShardAsHTML(t *testing.T) { s := &topodatapb.Shard_SourceShard{ Uid: 123, diff --git a/go/vt/topo/zk2topo/replication_graph.go b/go/vt/topo/zk2topo/replication_graph.go index 7120cda6bd8..66b127d358c 100644 --- a/go/vt/topo/zk2topo/replication_graph.go +++ b/go/vt/topo/zk2topo/replication_graph.go @@ -56,16 +56,21 @@ func (zs *Server) UpdateShardReplicationFields(ctx context.Context, cell, keyspa if version == nil { // We have to create, and we catch ErrNodeExists. _, err = zs.Create(ctx, cell, zkPath, data) - if err != topo.ErrNodeExists { - return err - } - } else { - // We have to update, and we catch ErrBadVersion. - _, err = zs.Update(ctx, cell, zkPath, data, version) - if err != topo.ErrBadVersion { - return err + if err == topo.ErrNodeExists { + // Node was created by another process, try + // again. + continue } + return err + } + + // We have to update, and we catch ErrBadVersion. + _, err = zs.Update(ctx, cell, zkPath, data, version) + if err == topo.ErrBadVersion { + // Node was updated by another process, try again. + continue } + return err } } diff --git a/go/vt/vitessdriver/fakeserver_test.go b/go/vt/vitessdriver/fakeserver_test.go index debea27de93..e9c28fd8c6e 100644 --- a/go/vt/vitessdriver/fakeserver_test.go +++ b/go/vt/vitessdriver/fakeserver_test.go @@ -29,10 +29,10 @@ type queryExecute struct { } // Execute is part of the VTGateService interface -func (f *fakeVTGateService) Execute(ctx context.Context, sql string, bindVariables map[string]interface{}, keyspace string, tabletType topodatapb.TabletType, session *vtgatepb.Session, notInTransaction bool, options *querypb.ExecuteOptions) (*sqltypes.Result, error) { +func (f *fakeVTGateService) Execute(ctx context.Context, sql string, bindVariables map[string]interface{}, keyspace string, tabletType topodatapb.TabletType, session *vtgatepb.Session, notInTransaction bool, options *querypb.ExecuteOptions) (*vtgatepb.Session, *sqltypes.Result, error) { execCase, ok := execMap[sql] if !ok { - return nil, fmt.Errorf("no match for: %s", sql) + return session, nil, fmt.Errorf("no match for: %s", sql) } query := &queryExecute{ SQL: sql, @@ -43,12 +43,12 @@ func (f *fakeVTGateService) Execute(ctx context.Context, sql string, bindVariabl NotInTransaction: notInTransaction, } if !reflect.DeepEqual(query, execCase.execQuery) { - return nil, fmt.Errorf("Execute request mismatch: got %+v, want %+v", query, execCase.execQuery) + return session, nil, fmt.Errorf("Execute request mismatch: got %+v, want %+v", query, execCase.execQuery) } if execCase.session != nil { *session = *execCase.session } - return execCase.result, nil + return session, execCase.result, nil } // ExecuteShards is part of the VTGateService interface @@ -72,11 +72,11 @@ func (f *fakeVTGateService) ExecuteEntityIds(ctx context.Context, sql string, bi } // ExecuteBatch is part of the VTGateService interface -func (f *fakeVTGateService) ExecuteBatch(ctx context.Context, sql []string, bindVariables []map[string]interface{}, keyspace string, tabletType topodatapb.TabletType, asTransaction bool, session *vtgatepb.Session, options *querypb.ExecuteOptions) ([]sqltypes.QueryResponse, error) { +func (f *fakeVTGateService) ExecuteBatch(ctx context.Context, sql []string, bindVariables []map[string]interface{}, keyspace string, tabletType topodatapb.TabletType, session *vtgatepb.Session, options *querypb.ExecuteOptions) (*vtgatepb.Session, []sqltypes.QueryResponse, error) { if len(sql) == 1 { execCase, ok := execMap[sql[0]] if !ok { - return nil, fmt.Errorf("no match for: %s", sql) + return session, nil, fmt.Errorf("no match for: %s", sql) } if bindVariables == nil { bindVariables = make([]map[string]interface{}, 1) @@ -89,16 +89,16 @@ func (f *fakeVTGateService) ExecuteBatch(ctx context.Context, sql []string, bind Session: session, } if !reflect.DeepEqual(query, execCase.execQuery) { - return nil, fmt.Errorf("Execute request mismatch: got %+v, want %+v", query, execCase.execQuery) + return session, nil, fmt.Errorf("Execute request mismatch: got %+v, want %+v", query, execCase.execQuery) } if execCase.session != nil { *session = *execCase.session } - return []sqltypes.QueryResponse{ + return session, []sqltypes.QueryResponse{ {QueryResult: execCase.result}, }, nil } - return nil, nil + return session, nil, nil } // ExecuteBatchShard is part of the VTGateService interface diff --git a/go/vt/vtctld/realtime_status_test.go b/go/vt/vtctld/realtime_status_test.go index 152f747fb6a..1e0230237fa 100644 --- a/go/vt/vtctld/realtime_status_test.go +++ b/go/vt/vtctld/realtime_status_test.go @@ -2,7 +2,6 @@ package vtctld import ( "fmt" - "reflect" "testing" "time" @@ -106,7 +105,7 @@ func checkStats(realtimeStats *realtimeStats, tablet *testlib.FakeTablet, want * if err != nil { continue } - if reflect.DeepEqual(result, discovery.TabletStats{}) { + if result.DeepEqual(&discovery.TabletStats{}) { continue } got := result.Stats diff --git a/go/vt/vtctld/tablet_stats_cache_test.go b/go/vt/vtctld/tablet_stats_cache_test.go index 7279329b8ba..98b8cb4f7fa 100644 --- a/go/vt/vtctld/tablet_stats_cache_test.go +++ b/go/vt/vtctld/tablet_stats_cache_test.go @@ -22,26 +22,26 @@ func TestStatsUpdate(t *testing.T) { // Insert tablet1. tabletStatsCache.StatsUpdate(tablet1Stats1) results1 := tabletStatsCache.statuses["ks1"]["-80"]["cell1"][topodatapb.TabletType_REPLICA] - if got, want := results1[0], tablet1Stats1; !reflect.DeepEqual(got, want) { + if got, want := results1[0], tablet1Stats1; !got.DeepEqual(want) { t.Errorf("got: %v, want: %v", got, want) } // Update tablet1. tabletStatsCache.StatsUpdate(tablet1Stats2) results2 := tabletStatsCache.statuses["ks1"]["-80"]["cell1"][topodatapb.TabletType_REPLICA] - if got, want := results2[0], tablet1Stats2; !reflect.DeepEqual(got, want) { + if got, want := results2[0], tablet1Stats2; !got.DeepEqual(want) { t.Errorf("got: %v, want: %v", got, want) } // Insert tablet. List of tablets will be resorted. tabletStatsCache.StatsUpdate(tablet2Stats1) results3 := tabletStatsCache.statuses["ks1"]["-80"]["cell1"][topodatapb.TabletType_REPLICA] - if got, want := results3[0], tablet2Stats1; !reflect.DeepEqual(got, want) { + if got, want := results3[0], tablet2Stats1; !got.DeepEqual(want) { t.Errorf("got: %v, want: %v", got, want) } results4 := tabletStatsCache.statuses["ks1"]["-80"]["cell1"][topodatapb.TabletType_REPLICA] - if got, want := results4[1], tablet1Stats2; !reflect.DeepEqual(got, want) { + if got, want := results4[1], tablet1Stats2; !got.DeepEqual(want) { t.Errorf("got: %v, want: %v", got, want) } @@ -50,7 +50,7 @@ func TestStatsUpdate(t *testing.T) { tabletStatsCache.StatsUpdate(tablet2Stats1) results5 := tabletStatsCache.statuses["ks1"]["-80"]["cell1"][topodatapb.TabletType_REPLICA] for _, stat := range results5 { - if reflect.DeepEqual(stat, tablet2Stats1) { + if stat.DeepEqual(tablet2Stats1) { t.Errorf("not deleleted from statusesByAliases") } } @@ -329,14 +329,14 @@ func TestTabletStats(t *testing.T) { // Test 1: tablet1 and tablet2 are updated with the stats received by the HealthCheck module. got1, err := tabletStatsCache.tabletStats(ts1.Tablet.Alias) - want1 := *ts1 - if err != nil || !reflect.DeepEqual(got1, want1) { + want1 := ts1 + if err != nil || !got1.DeepEqual(want1) { t.Errorf("got: %v, want: %v", got1, want1) } got2, err := tabletStatsCache.tabletStats(ts2.Tablet.Alias) - want2 := *ts2 - if err != nil || !reflect.DeepEqual(got2, want2) { + want2 := ts2 + if err != nil || !got2.DeepEqual(want2) { t.Errorf("got: %v, want: %v", got2, want2) } diff --git a/go/vt/vtgate/engine/primitive.go b/go/vt/vtgate/engine/primitive.go index 45ff8b4171e..66617a81808 100644 --- a/go/vt/vtgate/engine/primitive.go +++ b/go/vt/vtgate/engine/primitive.go @@ -30,6 +30,7 @@ type VCursor interface { GetShardForKeyspaceID(allShards []*topodatapb.ShardReference, keyspaceID []byte) (string, error) ExecuteShard(keyspace string, shardQueries map[string]querytypes.BoundQuery) (*sqltypes.Result, error) Execute(query string, bindvars map[string]interface{}) (*sqltypes.Result, error) + ExecuteShow(query string, bindvars map[string]interface{}, keyspace string) (*sqltypes.Result, error) } // Plan represents the execution strategy for a given query. diff --git a/go/vt/vtgate/engine/route.go b/go/vt/vtgate/engine/route.go index d67cc464289..91ff189fbac 100644 --- a/go/vt/vtgate/engine/route.go +++ b/go/vt/vtgate/engine/route.go @@ -191,6 +191,8 @@ const ( // for each ColVindex. If the table has an Autoinc column, // A Generate subplan must be created. InsertSharded + // Show is a pseudo-opcode used for SHOW commands + Show // NumCodes is the total number of opcodes for routes. NumCodes ) @@ -209,6 +211,7 @@ var routeName = [NumCodes]string{ "DeleteEqual", "InsertUnsharded", "InsertSharded", + "Metadata", } func (code RouteOpcode) String() string { @@ -257,6 +260,8 @@ func (route *Route) Execute(vcursor VCursor, queryConstruct *queryinfo.QueryCons return route.execInsertSharded(vcursor, queryConstruct) case InsertUnsharded: return route.execInsertUnsharded(vcursor, queryConstruct) + case Show: + return route.execShow(vcursor, queryConstruct) } var err error @@ -409,6 +414,10 @@ func (route *Route) execUpdateEqual(vcursor VCursor, queryConstruct *queryinfo.Q return vcursor.ScatterConnExecute(rewritten, queryConstruct.BindVars, ks, []string{shard}, queryConstruct.NotInTransaction) } +func (route *Route) execShow(vcursor VCursor, queryConstruct *queryinfo.QueryConstruct) (*sqltypes.Result, error) { + return vcursor.ExecuteShow(route.Query, queryConstruct.BindVars, queryConstruct.Keyspace) +} + func (route *Route) execDeleteEqual(vcursor VCursor, queryConstruct *queryinfo.QueryConstruct) (*sqltypes.Result, error) { keys, err := route.resolveKeys([]interface{}{route.Values}, queryConstruct.BindVars) if err != nil { diff --git a/go/vt/vtgate/grpcvtgateconn/conn.go b/go/vt/vtgate/grpcvtgateconn/conn.go index 92f4f83572f..494a0a73daa 100644 --- a/go/vt/vtgate/grpcvtgateconn/conn.go +++ b/go/vt/vtgate/grpcvtgateconn/conn.go @@ -58,7 +58,7 @@ func dial(ctx context.Context, addr string, timeout time.Duration) (vtgateconn.I }, nil } -func (conn *vtgateConn) Execute(ctx context.Context, query string, bindVars map[string]interface{}, keyspace string, tabletType topodatapb.TabletType, session interface{}, options *querypb.ExecuteOptions) (*sqltypes.Result, interface{}, error) { +func (conn *vtgateConn) Execute(ctx context.Context, query string, bindVars map[string]interface{}, keyspaceShard string, tabletType topodatapb.TabletType, session interface{}, options *querypb.ExecuteOptions) (*sqltypes.Result, interface{}, error) { var s *vtgatepb.Session if session != nil { s = session.(*vtgatepb.Session) @@ -68,12 +68,12 @@ func (conn *vtgateConn) Execute(ctx context.Context, query string, bindVars map[ return nil, session, err } request := &vtgatepb.ExecuteRequest{ - CallerId: callerid.EffectiveCallerIDFromContext(ctx), - Session: s, - Query: q, - Keyspace: keyspace, - TabletType: tabletType, - Options: options, + CallerId: callerid.EffectiveCallerIDFromContext(ctx), + Session: s, + Query: q, + KeyspaceShard: keyspaceShard, + TabletType: tabletType, + Options: options, } response, err := conn.c.Execute(ctx, request) if err != nil { @@ -198,7 +198,7 @@ func (conn *vtgateConn) ExecuteEntityIds(ctx context.Context, query string, keys return sqltypes.Proto3ToResult(response.Result), response.Session, nil } -func (conn *vtgateConn) ExecuteBatch(ctx context.Context, queryList []string, bindVarsList []map[string]interface{}, keyspace string, tabletType topodatapb.TabletType, asTransaction bool, session interface{}, options *querypb.ExecuteOptions) ([]sqltypes.QueryResponse, interface{}, error) { +func (conn *vtgateConn) ExecuteBatch(ctx context.Context, queryList []string, bindVarsList []map[string]interface{}, keyspaceShard string, tabletType topodatapb.TabletType, asTransaction bool, session interface{}, options *querypb.ExecuteOptions) ([]sqltypes.QueryResponse, interface{}, error) { var s *vtgatepb.Session if session != nil { s = session.(*vtgatepb.Session) @@ -211,7 +211,7 @@ func (conn *vtgateConn) ExecuteBatch(ctx context.Context, queryList []string, bi CallerId: callerid.EffectiveCallerIDFromContext(ctx), Session: s, Queries: q, - Keyspace: keyspace, + KeyspaceShard: keyspaceShard, TabletType: tabletType, AsTransaction: asTransaction, Options: options, @@ -288,17 +288,17 @@ func (a *streamExecuteAdapter) Recv() (*sqltypes.Result, error) { return sqltypes.CustomProto3ToResult(a.fields, qr), nil } -func (conn *vtgateConn) StreamExecute(ctx context.Context, query string, bindVars map[string]interface{}, keyspace string, tabletType topodatapb.TabletType, options *querypb.ExecuteOptions) (sqltypes.ResultStream, error) { +func (conn *vtgateConn) StreamExecute(ctx context.Context, query string, bindVars map[string]interface{}, keyspaceShard string, tabletType topodatapb.TabletType, options *querypb.ExecuteOptions) (sqltypes.ResultStream, error) { q, err := querytypes.BoundQueryToProto3(query, bindVars) if err != nil { return nil, err } req := &vtgatepb.StreamExecuteRequest{ - CallerId: callerid.EffectiveCallerIDFromContext(ctx), - Query: q, - Keyspace: keyspace, - TabletType: tabletType, - Options: options, + CallerId: callerid.EffectiveCallerIDFromContext(ctx), + Query: q, + KeyspaceShard: keyspaceShard, + TabletType: tabletType, + Options: options, } stream, err := conn.c.StreamExecute(ctx, req) if err != nil { diff --git a/go/vt/vtgate/grpcvtgateservice/server.go b/go/vt/vtgate/grpcvtgateservice/server.go index aefd83127de..4f0076af9c3 100644 --- a/go/vt/vtgate/grpcvtgateservice/server.go +++ b/go/vt/vtgate/grpcvtgateservice/server.go @@ -91,10 +91,10 @@ func (vtg *VTGate) Execute(ctx context.Context, request *vtgatepb.ExecuteRequest if err != nil { return nil, vterrors.ToGRPC(err) } - result, err := vtg.server.Execute(ctx, string(request.Query.Sql), bv, request.Keyspace, request.TabletType, request.Session, request.NotInTransaction, request.Options) + session, result, err := vtg.server.Execute(ctx, string(request.Query.Sql), bv, request.KeyspaceShard, request.TabletType, request.Session, request.NotInTransaction, request.Options) return &vtgatepb.ExecuteResponse{ Result: sqltypes.ResultToProto3(result), - Session: request.Session, + Session: session, Error: vterrors.ToVTRPC(err), }, nil } @@ -211,10 +211,10 @@ func (vtg *VTGate) ExecuteBatch(ctx context.Context, request *vtgatepb.ExecuteBa sqlQueries[queryNum] = query.Sql bindVars[queryNum] = bv } - results, err = vtg.server.ExecuteBatch(ctx, sqlQueries, bindVars, request.Keyspace, request.TabletType, request.AsTransaction, request.Session, request.Options) + session, results, err := vtg.server.ExecuteBatch(ctx, sqlQueries, bindVars, request.KeyspaceShard, request.TabletType, request.Session, request.Options) return &vtgatepb.ExecuteBatchResponse{ Results: sqltypes.QueryResponsesToProto3(results), - Session: request.Session, + Session: session, Error: vterrors.ToVTRPC(err), }, nil } @@ -265,7 +265,7 @@ func (vtg *VTGate) StreamExecute(request *vtgatepb.StreamExecuteRequest, stream vtgErr := vtg.server.StreamExecute(ctx, string(request.Query.Sql), bv, - request.Keyspace, + request.KeyspaceShard, request.TabletType, request.Options, func(value *sqltypes.Result) error { diff --git a/go/vt/vtgate/planbuilder/builder.go b/go/vt/vtgate/planbuilder/builder.go index 81ebeee007e..4a412f87611 100644 --- a/go/vt/vtgate/planbuilder/builder.go +++ b/go/vt/vtgate/planbuilder/builder.go @@ -106,7 +106,9 @@ func BuildFromStmt(query string, stmt sqlparser.Statement, vschema VSchema) (*en plan.Instructions, err = buildUpdatePlan(stmt, vschema) case *sqlparser.Delete: plan.Instructions, err = buildDeletePlan(stmt, vschema) - case *sqlparser.Union, *sqlparser.Set, *sqlparser.DDL, *sqlparser.Other: + case *sqlparser.Show: + plan.Instructions, err = buildShowPlan(stmt, vschema) + case *sqlparser.Union, *sqlparser.DDL, *sqlparser.Set, *sqlparser.Other: return nil, errors.New("unsupported construct") default: panic("unexpected statement type") diff --git a/go/vt/vtgate/planbuilder/plan_test.go b/go/vt/vtgate/planbuilder/plan_test.go index 773f4336fa7..e12ce90a629 100644 --- a/go/vt/vtgate/planbuilder/plan_test.go +++ b/go/vt/vtgate/planbuilder/plan_test.go @@ -100,6 +100,7 @@ func TestPlan(t *testing.T) { testFile(t, "postprocess_cases.txt", vschema) testFile(t, "wireup_cases.txt", vschema) testFile(t, "dml_cases.txt", vschema) + testFile(t, "show_cases.txt", vschema) testFile(t, "unsupported_cases.txt", vschema) } @@ -141,7 +142,7 @@ func testFile(t *testing.T, filename string, vschema *vindexes.VSchema) { out = string(bout) } if out != tcase.output { - t.Errorf("File: %s, Line:%v\n%s\n%s", filename, tcase.lineno, tcase.output, out) + t.Errorf("File: %s, Line:%v\ngot = %s\nwant = %s", filename, tcase.lineno, out, tcase.output) // Uncomment these lines to re-generate input files if err != nil { out = fmt.Sprintf("\"%s\"", out) @@ -211,8 +212,11 @@ func iterateExecFile(name string) (testCaseIterator chan testCase) { if l[0] == '}' { output = output[:len(output)-1] b := bytes.NewBuffer(make([]byte, 0, 64)) - if err := json.Compact(b, output); err == nil { + err := json.Compact(b, output) + if err == nil { output = b.Bytes() + } else { + panic("Invalid JSON " + string(output) + err.Error()) } break } diff --git a/go/vt/vtgate/planbuilder/show.go b/go/vt/vtgate/planbuilder/show.go new file mode 100644 index 00000000000..acd6c196ac4 --- /dev/null +++ b/go/vt/vtgate/planbuilder/show.go @@ -0,0 +1,26 @@ +// Copyright 2016, Google Inc. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package planbuilder + +import ( + "errors" + + "github.com/youtube/vitess/go/vt/sqlparser" + "github.com/youtube/vitess/go/vt/vtgate/engine" +) + +// buildSelectPlan is the new function to build a Select plan. +func buildShowPlan(node *sqlparser.Show, vschema VSchema) (primitive engine.Primitive, err error) { + if (node.Type == sqlparser.ShowUnsupportedStr){ + return nil, errors.New("unsupported show statement") + } + + route := &engine.Route{ + Opcode: engine.Show, + Query: node.Type, + } + + return route, nil +} diff --git a/go/vt/vtgate/plugin_mysql_server.go b/go/vt/vtgate/plugin_mysql_server.go index fd9973c0d30..11a588c3718 100644 --- a/go/vt/vtgate/plugin_mysql_server.go +++ b/go/vt/vtgate/plugin_mysql_server.go @@ -4,7 +4,6 @@ import ( "flag" "fmt" "net" - "strings" log "github.com/golang/glog" "golang.org/x/net/context" @@ -64,64 +63,11 @@ func (vh *vtgateHandler) NewConnection(c *mysqlconn.Conn) { func (vh *vtgateHandler) ConnectionClosed(c *mysqlconn.Conn) { // Rollback if there is an ongoing transaction. Ignore error. ctx := context.Background() - vh.rollback(ctx, c) -} - -func (vh *vtgateHandler) begin(ctx context.Context, c *mysqlconn.Conn) (*sqltypes.Result, error) { - // Check we're not inside a transaction already. - if c.ClientData != nil { - return nil, sqldb.NewSQLError(mysqlconn.ERCantDoThisDuringAnTransaction, mysqlconn.SSCantDoThisDuringAnTransaction, "already in a transaction") - } - - // Do the begin. - session, err := vh.vtg.Begin(ctx, false /* singledb */) - if err != nil { - return nil, sqldb.NewSQLError(mysqlconn.ERUnknownError, mysqlconn.SSUnknownSQLState, "vtgate.Begin failed: %v", err) - } - - // Save the session. - c.ClientData = session - return &sqltypes.Result{}, nil -} - -func (vh *vtgateHandler) commit(ctx context.Context, c *mysqlconn.Conn) (*sqltypes.Result, error) { - // Check we're inside a transaction already. - if c.ClientData == nil { - return nil, sqldb.NewSQLError(mysqlconn.ERUnknownError, mysqlconn.SSUnknownSQLState, "not in a transaction") - } - session, ok := c.ClientData.(*vtgatepb.Session) - if !ok || session == nil { - return nil, sqldb.NewSQLError(mysqlconn.ERUnknownError, mysqlconn.SSUnknownSQLState, "internal error: got a weird ClientData of type %T: %v %v", c.ClientData, session, ok) - } - - // Commit using vtgate's transaction mode. - if err := vh.vtg.Commit(ctx, vh.vtg.transactionMode == TxTwoPC, session); err != nil { - return nil, sqldb.NewSQLError(mysqlconn.ERUnknownError, mysqlconn.SSUnknownSQLState, "vtgate.Commit failed: %v", err) - } - - // Clear the Session. - c.ClientData = nil - return &sqltypes.Result{}, nil -} - -func (vh *vtgateHandler) rollback(ctx context.Context, c *mysqlconn.Conn) (*sqltypes.Result, error) { - // Check we're inside a transaction already. - if c.ClientData == nil { - return nil, sqldb.NewSQLError(mysqlconn.ERUnknownError, mysqlconn.SSUnknownSQLState, "not in a transaction") - } - session, ok := c.ClientData.(*vtgatepb.Session) - if !ok || session == nil { - return nil, sqldb.NewSQLError(mysqlconn.ERUnknownError, mysqlconn.SSUnknownSQLState, "internal error: got a weird ClientData of type %T: %v %v", c.ClientData, session, ok) - } - - // Rollback. - if err := vh.vtg.Rollback(ctx, session); err != nil { - return nil, sqldb.NewSQLError(mysqlconn.ERUnknownError, mysqlconn.SSUnknownSQLState, "vtgate.Rollback failed: %v", err) + session, _ := c.ClientData.(*vtgatepb.Session) + if session == nil || !session.InTransaction { + return } - - // Clear the Session. - c.ClientData = nil - return &sqltypes.Result{}, nil + _, _, _ = vh.vtg.Execute(ctx, "rollback", make(map[string]interface{}), "", topodatapb.TabletType_MASTER, session, false, &querypb.ExecuteOptions{}) } func (vh *vtgateHandler) ComQuery(c *mysqlconn.Conn, query string) (*sqltypes.Result, error) { @@ -143,30 +89,12 @@ func (vh *vtgateHandler) ComQuery(c *mysqlconn.Conn, query string) (*sqltypes.Re "VTGate MySQL Connector" /* subcomponent: part of the client */) ctx = callerid.NewContext(ctx, ef, im) - // FIXME(alainjobart) would be good to have the parser understand this. - switch { - case strings.EqualFold(query, "begin"): - return vh.begin(ctx, c) - case strings.EqualFold(query, "commit"): - return vh.commit(ctx, c) - case strings.EqualFold(query, "rollback"): - return vh.rollback(ctx, c) - case strings.EqualFold(query, "set autocommit=0"): - // This is done by the python MySQL connector, we ignore it. - return &sqltypes.Result{}, nil - default: - // Grab the current session, if any. - var session *vtgatepb.Session - if c.ClientData != nil { - session, _ = c.ClientData.(*vtgatepb.Session) - } - - // And just go to v3. - result, err := vh.vtg.Execute(ctx, query, make(map[string]interface{}), c.SchemaName, topodatapb.TabletType_MASTER, session, false /* notInTransaction */, &querypb.ExecuteOptions{ - IncludedFields: querypb.ExecuteOptions_ALL, - }) - return result, sqldb.NewSQLErrorFromError(err) - } + session, _ := c.ClientData.(*vtgatepb.Session) + session, result, err := vh.vtg.Execute(ctx, query, make(map[string]interface{}), c.SchemaName, topodatapb.TabletType_MASTER, session, false /* notInTransaction */, &querypb.ExecuteOptions{ + IncludedFields: querypb.ExecuteOptions_ALL, + }) + c.ClientData = session + return result, sqldb.NewSQLErrorFromError(err) } func init() { diff --git a/go/vt/vtgate/query_executor.go b/go/vt/vtgate/query_executor.go index 2751334b047..853649aa795 100644 --- a/go/vt/vtgate/query_executor.go +++ b/go/vt/vtgate/query_executor.go @@ -5,13 +5,18 @@ package vtgate import ( + "sort" + "golang.org/x/net/context" "github.com/youtube/vitess/go/sqltypes" + "github.com/youtube/vitess/go/vt/sqlparser" + "github.com/youtube/vitess/go/vt/vterrors" querypb "github.com/youtube/vitess/go/vt/proto/query" topodatapb "github.com/youtube/vitess/go/vt/proto/topodata" vtgatepb "github.com/youtube/vitess/go/vt/proto/vtgate" + vtrpcpb "github.com/youtube/vitess/go/vt/proto/vtrpc" "github.com/youtube/vitess/go/vt/vttablet/tabletserver/querytypes" ) @@ -73,3 +78,105 @@ func (vc *queryExecutor) GetShardForKeyspaceID(allShards []*topodatapb.ShardRefe func (vc *queryExecutor) ExecuteShard(keyspace string, shardQueries map[string]querytypes.BoundQuery) (*sqltypes.Result, error) { return vc.router.scatterConn.ExecuteMultiShard(vc.ctx, keyspace, shardQueries, vc.tabletType, NewSafeSession(nil), false, vc.options) } + +func (vc *queryExecutor) ExecuteShow(query string, bindvars map[string]interface{}, keyspace string) (*sqltypes.Result, error) { + if query == sqlparser.ShowDatabasesStr || query == sqlparser.ShowKeyspacesStr { + keyspaces, err := getAllKeyspaces(vc.ctx, vc.router.serv, vc.router.cell) + if err != nil { + return nil, err + } + + rows := make([][]sqltypes.Value, len(keyspaces)) + for i, v := range keyspaces { + rows[i] = []sqltypes.Value{sqltypes.MakeString([]byte(v))} + } + + result := &sqltypes.Result{ + Fields: []*querypb.Field{{ + Name: "Databases", + Type: sqltypes.VarChar, + }}, + RowsAffected: uint64(len(keyspaces)), + InsertID: 0, + Rows: rows, + } + + return result, nil + } + + if query == sqlparser.ShowShardsStr { + keyspaces, err := getAllKeyspaces(vc.ctx, vc.router.serv, vc.router.cell) + if err != nil { + return nil, err + } + + var shards []string + + for _, keyspace := range keyspaces { + _, _, ks_shards, err := getKeyspaceShards(vc.ctx, vc.router.serv, vc.router.cell, keyspace, vc.tabletType) + if err != nil { + return nil, err + } + + for _, shard := range ks_shards { + shards = append(shards, keyspace+"/"+shard.Name) + } + } + + rows := make([][]sqltypes.Value, len(shards)) + for i, v := range shards { + rows[i] = []sqltypes.Value{sqltypes.MakeString([]byte(v))} + } + + result := &sqltypes.Result{ + Fields: []*querypb.Field{{ + Name: "Shards", + Type: sqltypes.VarChar, + }}, + RowsAffected: uint64(len(shards)), + InsertID: 0, + Rows: rows, + } + + return result, nil + } + + if query == sqlparser.ShowVSchemaTablesStr { + if keyspace == "" { + return nil, vterrors.Errorf(vtrpcpb.Code_INVALID_ARGUMENT, "No keyspace selected") + } + vschema := vc.router.planner.VSchema() + if vschema == nil { + return nil, vterrors.Errorf(vtrpcpb.Code_FAILED_PRECONDITION, "vschema not initialized") + } + ks, ok := vschema.Keyspaces[keyspace] + if !ok { + return nil, vterrors.Errorf(vtrpcpb.Code_NOT_FOUND, "keyspace %s not found in vschema", keyspace) + } + + var tables []string; + for name := range ks.Tables { + tables = append(tables, name) + } + sort.Strings(tables) + + rows := make([][]sqltypes.Value, len(tables)) + for i, v := range tables { + rows[i] = []sqltypes.Value{sqltypes.MakeString([]byte(v))} + } + + result := &sqltypes.Result{ + Fields: []*querypb.Field{{ + Name: "Tables", + Type: sqltypes.VarChar, + }}, + RowsAffected: uint64(len(rows)), + InsertID: 0, + Rows: rows, + } + + return result, nil + } + + return nil, vterrors.Errorf(vtrpcpb.Code_UNIMPLEMENTED, "unimplemented metadata query: "+query) +} diff --git a/go/vt/vtgate/resolver.go b/go/vt/vtgate/resolver.go index 8afc2f99838..145e94ef2bc 100644 --- a/go/vt/vtgate/resolver.go +++ b/go/vt/vtgate/resolver.go @@ -14,7 +14,7 @@ import ( "strings" "github.com/youtube/vitess/go/sqltypes" - "github.com/youtube/vitess/go/vt/sqlannotation" + "github.com/youtube/vitess/go/vt/sqlparser" "github.com/youtube/vitess/go/vt/topo" "github.com/youtube/vitess/go/vt/vterrors" "github.com/youtube/vitess/go/vt/vtgate/gateway" @@ -62,7 +62,7 @@ func isRetryableError(err error) bool { // This throws an error if a dml spans multiple keyspace_ids. Resharding depends // on being able to uniquely route a write. func (res *Resolver) ExecuteKeyspaceIds(ctx context.Context, sql string, bindVariables map[string]interface{}, keyspace string, keyspaceIds [][]byte, tabletType topodatapb.TabletType, session *vtgatepb.Session, notInTransaction bool, options *querypb.ExecuteOptions) (*sqltypes.Result, error) { - if sqlannotation.IsDML(sql) && len(keyspaceIds) > 1 { + if sqlparser.IsDML(sql) && len(keyspaceIds) > 1 { return nil, vterrors.New(vtrpcpb.Code_INVALID_ARGUMENT, "DML should not span multiple keyspace_ids") } mapToShards := func(k string) (string, []string, error) { diff --git a/go/vt/vtgate/router.go b/go/vt/vtgate/router.go index 6118d65a527..160147a1cce 100644 --- a/go/vt/vtgate/router.go +++ b/go/vt/vtgate/router.go @@ -65,35 +65,6 @@ func (rtr *Router) StreamExecute(ctx context.Context, sql string, bindVars map[s return plan.Instructions.StreamExecute(vcursor, queryConstruct, make(map[string]interface{}), true, callback) } -// ExecuteBatch routes a non-streaming queries. -func (rtr *Router) ExecuteBatch(ctx context.Context, sqlList []string, bindVarsList []map[string]interface{}, keyspace string, tabletType topodatapb.TabletType, asTransaction bool, session *vtgatepb.Session, options *querypb.ExecuteOptions) ([]sqltypes.QueryResponse, error) { - if bindVarsList == nil { - bindVarsList = make([]map[string]interface{}, len(sqlList)) - } - queryResponseList := make([]sqltypes.QueryResponse, 0, len(sqlList)) - for sqlNum, query := range sqlList { - var queryResponse sqltypes.QueryResponse - - bindVars := bindVarsList[sqlNum] - if bindVars == nil { - bindVars = make(map[string]interface{}) - } - //Using same QueryExecutor -> marking notInTransaction as false and not using asTransaction flag - vcursor := newQueryExecutor(ctx, tabletType, session, options, rtr) - queryConstruct := queryinfo.NewQueryConstruct(query, keyspace, bindVars, false) - plan, err := rtr.planner.GetPlan(query, keyspace, bindVars) - if err != nil { - queryResponse.QueryError = err - } else { - result, err := plan.Instructions.Execute(vcursor, queryConstruct, make(map[string]interface{}), true) - queryResponse.QueryResult = result - queryResponse.QueryError = err - } - queryResponseList = append(queryResponseList, queryResponse) - } - return queryResponseList, nil -} - // MessageAck acks messages. func (rtr *Router) MessageAck(ctx context.Context, keyspace, name string, ids []*querypb.Value) (int64, error) { vschema := rtr.planner.VSchema() diff --git a/go/vt/vtgate/topo_utils.go b/go/vt/vtgate/topo_utils.go index 31f86a37e57..f234961353d 100644 --- a/go/vt/vtgate/topo_utils.go +++ b/go/vt/vtgate/topo_utils.go @@ -51,6 +51,15 @@ func getAnyShard(ctx context.Context, topoServ topo.SrvTopoServer, cell, keyspac return keyspace, allShards[0].Name, nil } +func getAllKeyspaces(ctx context.Context, topoServ topo.SrvTopoServer, cell string) ([]string, error) { + keyspaces, err := topoServ.GetSrvKeyspaceNames(ctx, cell) + if err != nil { + return nil, vterrors.Errorf(vtrpcpb.Code_UNKNOWN, "keyspace names fetch error: %v", err) + } + + return keyspaces, nil +} + func getKeyspaceShards(ctx context.Context, topoServ topo.SrvTopoServer, cell, keyspace string, tabletType topodatapb.TabletType) (string, *topodatapb.SrvKeyspace, []*topodatapb.ShardReference, error) { srvKeyspace, err := topoServ.GetSrvKeyspace(ctx, cell, keyspace) if err != nil { diff --git a/go/vt/vtgate/tx_conn.go b/go/vt/vtgate/tx_conn.go index 9335f23bcb2..91475bb010b 100644 --- a/go/vt/vtgate/tx_conn.go +++ b/go/vt/vtgate/tx_conn.go @@ -40,6 +40,8 @@ func (txc *TxConn) Commit(ctx context.Context, twopc bool, session *SafeSession) if !session.InTransaction() { return vterrors.New(vtrpcpb.Code_ABORTED, "cannot commit: not in transaction") } + defer session.Reset() + if twopc { return txc.commit2PC(ctx, session) } @@ -58,7 +60,6 @@ func (txc *TxConn) commitNormal(ctx context.Context, session *SafeSession) error committing = false } } - session.Reset() return err } @@ -115,6 +116,7 @@ func (txc *TxConn) Rollback(ctx context.Context, session *SafeSession) error { return nil } defer session.Reset() + return txc.runSessions(session.ShardSessions, func(s *vtgatepb.Session_ShardSession) error { return txc.gateway.Rollback(ctx, s.Target, s.TransactionId) }) diff --git a/go/vt/vtgate/vtgate.go b/go/vt/vtgate/vtgate.go index c682ba645c9..bc493ff04f9 100644 --- a/go/vt/vtgate/vtgate.go +++ b/go/vt/vtgate/vtgate.go @@ -25,6 +25,7 @@ import ( "github.com/youtube/vitess/go/vt/logutil" "github.com/youtube/vitess/go/vt/servenv" "github.com/youtube/vitess/go/vt/sqlannotation" + "github.com/youtube/vitess/go/vt/sqlparser" "github.com/youtube/vitess/go/vt/topo" "github.com/youtube/vitess/go/vt/topo/topoproto" "github.com/youtube/vitess/go/vt/vterrors" @@ -41,7 +42,7 @@ import ( var ( transactionMode = flag.String("transaction_mode", "multi", "single: disallow multi-db transactions, multi: allow multi-db transactions with best effort commit, twopc: allow multi-db transactions with 2pc commit") - normalizeQueries = flag.Bool("normalize_queries", false, "Turning this flag on will cause vtgate to rewrite queries with bind vars. This is beneficial if the app doesn't itself send normalized queries.") + normalizeQueries = flag.Bool("normalize_queries", true, "Rewrite queries with bind vars. Turn this off if the app itself sends normalized queries with bind vars.") ) // Transaction modes. The value specifies what's allowed. @@ -220,29 +221,125 @@ func (vtg *VTGate) IsHealthy() error { } // Execute executes a non-streaming query by routing based on the values in the query. -func (vtg *VTGate) Execute(ctx context.Context, sql string, bindVariables map[string]interface{}, keyspace string, tabletType topodatapb.TabletType, session *vtgatepb.Session, notInTransaction bool, options *querypb.ExecuteOptions) (*sqltypes.Result, error) { +func (vtg *VTGate) Execute(ctx context.Context, sql string, bindVariables map[string]interface{}, keyspaceShard string, tabletType topodatapb.TabletType, session *vtgatepb.Session, notInTransaction bool, options *querypb.ExecuteOptions) (newSession *vtgatepb.Session, qr *sqltypes.Result, err error) { + // We'll always return a non-nil session. + if session == nil { + session = &vtgatepb.Session{} + } + + session, intercepted, err := vtg.intercept(ctx, sql, session) + if err != nil { + return session, nil, err + } + if intercepted { + return session, &sqltypes.Result{}, nil + } + + // Normal processing. startTime := time.Now() ltt := topoproto.TabletTypeLString(tabletType) statsKey := []string{"Execute", "Any", ltt} defer vtg.timings.Record(statsKey, startTime) - qr, err := vtg.router.Execute(ctx, sql, bindVariables, keyspace, tabletType, session, notInTransaction, options) + // Autocommit handling + autocommit := false + if session.Autocommit && !session.InTransaction && sqlparser.IsDML(sql) { + autocommit = true + vtg.localBegin(session) + } + + keyspace, shard := topoproto.ParseKeyspaceOptionalShard(keyspaceShard) + if shard != "" { + sql = sqlannotation.AnnotateIfDML(sql, nil) + f := func(keyspace string) (string, []string, error) { + return keyspace, []string{shard}, nil + } + qr, err = vtg.resolver.Execute(ctx, sql, bindVariables, keyspace, tabletType, session, f, notInTransaction, options) + } else { + qr, err = vtg.router.Execute(ctx, sql, bindVariables, keyspace, tabletType, session, notInTransaction, options) + } + if err == nil && autocommit { + // Set the error if commit fails. + err = vtg.Commit(ctx, vtg.transactionMode == TxTwoPC, session) + } if err == nil { vtg.rowsReturned.Add(statsKey, int64(len(qr.Rows))) - return qr, nil + return session, qr, nil + } + + // Error handling: Execute or Commit failed. + if autocommit { + // Rollback the transaction and ignore errors. + vtg.Rollback(ctx, session) } query := map[string]interface{}{ "Sql": sql, "BindVariables": bindVariables, - "Keyspace": keyspace, + "KeyspaceShard": keyspaceShard, "TabletType": ltt, "Session": session, "NotInTransaction": notInTransaction, "Options": options, } err = recordAndAnnotateError(err, statsKey, query, vtg.logExecute) - return nil, err + return session, nil, err +} + +// intercept checks for transactional or set statements. If they match then it performs the +// necessary operation and returns a new session and true indicating that it's intercepted +// the call. If so, the caller (Execute) should just return without proceeding further. +func (vtg *VTGate) intercept(ctx context.Context, sql string, session *vtgatepb.Session) (*vtgatepb.Session, bool, error) { + switch { + case sqlparser.IsStatement(sql, "begin"): + if session.InTransaction { + // If we're in a transaction, commit and start a new one. + if err := vtg.Commit(ctx, vtg.transactionMode == TxTwoPC, session); err != nil { + return session, true, err + } + } + vtg.localBegin(session) + return session, true, nil + case sqlparser.IsStatement(sql, "commit"): + if !session.InTransaction { + return session, true, nil + } + err := vtg.Commit(ctx, vtg.transactionMode == TxTwoPC, session) + return session, true, err + case sqlparser.IsStatement(sql, "rollback"): + if !session.InTransaction { + return session, true, nil + } + err := vtg.Rollback(ctx, session) + return session, true, err + case sqlparser.HasPrefix(sql, "set"): + vals, err := sqlparser.ExtractSetNums(sql) + if err != nil { + return session, true, vterrors.New(vtrpcpb.Code_INVALID_ARGUMENT, err.Error()) + } + if len(vals) != 1 { + return session, true, vterrors.Errorf(vtrpcpb.Code_INVALID_ARGUMENT, "too many set values: %s", sql) + } + val, ok := vals["autocommit"] + if !ok { + return session, true, vterrors.Errorf(vtrpcpb.Code_INVALID_ARGUMENT, "unsupported construct: %s", sql) + } + if val != 0 { + session.Autocommit = true + } else { + session.Autocommit = false + } + return session, true, nil + } + return session, false, nil +} + +// localBegin starts a transaction using the default settings of VTGate. +// This is different from the exported Begin because there is no explicit +// transaction mode when we implicitly begin a transaction. +func (vtg *VTGate) localBegin(session *vtgatepb.Session) { + session.InTransaction = true + session.SingleDb = vtg.transactionMode == TxSingle } // ExecuteShards executes a non-streaming query on the specified shards. @@ -375,33 +472,29 @@ func (vtg *VTGate) ExecuteEntityIds(ctx context.Context, sql string, bindVariabl } // ExecuteBatch executes a non-streaming queries by routing based on the values in the query. -func (vtg *VTGate) ExecuteBatch(ctx context.Context, sqlList []string, bindVariablesList []map[string]interface{}, keyspace string, tabletType topodatapb.TabletType, asTransaction bool, session *vtgatepb.Session, options *querypb.ExecuteOptions) ([]sqltypes.QueryResponse, error) { +func (vtg *VTGate) ExecuteBatch(ctx context.Context, sqlList []string, bindVariablesList []map[string]interface{}, keyspaceShard string, tabletType topodatapb.TabletType, session *vtgatepb.Session, options *querypb.ExecuteOptions) (*vtgatepb.Session, []sqltypes.QueryResponse, error) { + // We'll always return a non-nil session. + if session == nil { + session = &vtgatepb.Session{} + } + startTime := time.Now() ltt := topoproto.TabletTypeLString(tabletType) statsKey := []string{"ExecuteBatch", "Any", ltt} defer vtg.timings.Record(statsKey, startTime) - qr, err := vtg.router.ExecuteBatch(ctx, sqlList, bindVariablesList, keyspace, tabletType, asTransaction, session, options) - if err == nil { - for _, queryResponse := range qr { - if queryResponse.QueryResult != nil { - vtg.rowsReturned.Add(statsKey, int64(len(queryResponse.QueryResult.Rows))) - } + qrl := make([]sqltypes.QueryResponse, len(sqlList)) + for i, sql := range sqlList { + var bv map[string]interface{} + if len(bindVariablesList) != 0 { + bv = bindVariablesList[i] + } + session, qrl[i].QueryResult, qrl[i].QueryError = vtg.Execute(ctx, sql, bv, keyspaceShard, tabletType, session, false, options) + if qr := qrl[i].QueryResult; qr != nil { + vtg.rowsReturned.Add(statsKey, int64(len(qr.Rows))) } - return qr, nil - } - - query := map[string]interface{}{ - "Sql": sqlList, - "BindVariables": bindVariablesList, - "Keyspace": keyspace, - "TabletType": ltt, - "Session": session, - "AsTransaction": asTransaction, - "Options": options, } - err = recordAndAnnotateError(err, statsKey, query, vtg.logExecute) - return nil, err + return session, qrl, nil } // ExecuteBatchShards executes a group of queries on the specified shards. @@ -479,29 +572,48 @@ func (vtg *VTGate) ExecuteBatchKeyspaceIds(ctx context.Context, queries []*vtgat } // StreamExecute executes a streaming query by routing based on the values in the query. -func (vtg *VTGate) StreamExecute(ctx context.Context, sql string, bindVariables map[string]interface{}, keyspace string, tabletType topodatapb.TabletType, options *querypb.ExecuteOptions, callback func(*sqltypes.Result) error) error { +func (vtg *VTGate) StreamExecute(ctx context.Context, sql string, bindVariables map[string]interface{}, keyspaceShard string, tabletType topodatapb.TabletType, options *querypb.ExecuteOptions, callback func(*sqltypes.Result) error) error { startTime := time.Now() ltt := topoproto.TabletTypeLString(tabletType) statsKey := []string{"StreamExecute", "Any", ltt} defer vtg.timings.Record(statsKey, startTime) - err := vtg.router.StreamExecute( - ctx, - sql, - bindVariables, - keyspace, - tabletType, - options, - func(reply *sqltypes.Result) error { - vtg.rowsReturned.Add(statsKey, int64(len(reply.Rows))) - return callback(reply) - }) + keyspace, shard := topoproto.ParseKeyspaceOptionalShard(keyspaceShard) + var err error + if shard != "" { + err = vtg.resolver.streamExecute( + ctx, + sql, + bindVariables, + keyspace, + tabletType, + func(keyspace string) (string, []string, error) { + return keyspace, []string{shard}, nil + }, + options, + func(reply *sqltypes.Result) error { + vtg.rowsReturned.Add(statsKey, int64(len(reply.Rows))) + return callback(reply) + }) + } else { + err = vtg.router.StreamExecute( + ctx, + sql, + bindVariables, + keyspace, + tabletType, + options, + func(reply *sqltypes.Result) error { + vtg.rowsReturned.Add(statsKey, int64(len(reply.Rows))) + return callback(reply) + }) + } if err != nil { query := map[string]interface{}{ "Sql": sql, "BindVariables": bindVariables, - "Keyspace": keyspace, + "KeyspaceShard": keyspaceShard, "TabletType": ltt, "Options": options, } diff --git a/go/vt/vtgate/vtgate_test.go b/go/vt/vtgate/vtgate_test.go index 0d92e0fbb10..0cdf336ca96 100644 --- a/go/vt/vtgate/vtgate_test.go +++ b/go/vt/vtgate/vtgate_test.go @@ -195,7 +195,7 @@ func TestVTGateExecute(t *testing.T) { createSandbox(KsTestUnsharded) hcVTGateTest.Reset() sbc := hcVTGateTest.AddTestTablet("aa", "1.1.1.1", 1001, KsTestUnsharded, "0", topodatapb.TabletType_MASTER, true, 1, nil) - qr, err := rpcVTGate.Execute(context.Background(), + _, qr, err := rpcVTGate.Execute(context.Background(), "select id from t1", nil, "", @@ -260,11 +260,13 @@ func TestVTGateExecute(t *testing.T) { } } -func TestVTGateExecuteWithKeyspace(t *testing.T) { +func TestVTGateExecuteWithKeyspaceShard(t *testing.T) { createSandbox(KsTestUnsharded) hcVTGateTest.Reset() hcVTGateTest.AddTestTablet("aa", "1.1.1.1", 1001, KsTestUnsharded, "0", topodatapb.TabletType_MASTER, true, 1, nil) - qr, err := rpcVTGate.Execute(context.Background(), + + // Valid keyspace. + _, qr, err := rpcVTGate.Execute(context.Background(), "select id from none", nil, KsTestUnsharded, @@ -278,20 +280,259 @@ func TestVTGateExecuteWithKeyspace(t *testing.T) { if !reflect.DeepEqual(sandboxconn.SingleRowResult, qr) { t.Errorf("want \n%+v, got \n%+v", sandboxconn.SingleRowResult, qr) } - _, err = rpcVTGate.Execute(context.Background(), + + // Invalid keyspace. + _, _, err = rpcVTGate.Execute(context.Background(), + "select id from none", + nil, + "invalid_keyspace", + topodatapb.TabletType_MASTER, + nil, + false, + nil) + want := "vtgate: : keyspace invalid_keyspace not found in vschema" + if err == nil || err.Error() != want { + t.Errorf("Execute: %v, want %s", err, want) + } + + // Valid keyspace/shard. + _, qr, err = rpcVTGate.Execute(context.Background(), + "random statement", + nil, + KsTestUnsharded+"/0", + topodatapb.TabletType_MASTER, + nil, + false, + nil) + if err != nil { + t.Errorf("want nil, got %v", err) + } + if !reflect.DeepEqual(sandboxconn.SingleRowResult, qr) { + t.Errorf("want \n%+v, got \n%+v", sandboxconn.SingleRowResult, qr) + } + + // Invalid keyspace/shard. + _, _, err = rpcVTGate.Execute(context.Background(), "select id from none", nil, - "aa", + KsTestUnsharded+"/noshard", topodatapb.TabletType_MASTER, nil, false, nil) - want := "vtgate: : keyspace aa not found in vschema" + want = "vtgate: : target: TestUnsharded.noshard.master, no valid tablet" if err == nil || err.Error() != want { t.Errorf("Execute: %v, want %s", err, want) } } +func TestVTGateIntercept(t *testing.T) { + createSandbox(KsTestUnsharded) + hcVTGateTest.Reset() + sbc := hcVTGateTest.AddTestTablet("aa", "1.1.1.1", 1001, KsTestUnsharded, "0", topodatapb.TabletType_MASTER, true, 1, nil) + + // begin. + session, _, err := rpcVTGate.Execute(context.Background(), "begin", nil, "", topodatapb.TabletType_MASTER, nil, false, nil) + if err != nil { + t.Fatal(err) + } + wantSession := &vtgatepb.Session{InTransaction: true} + if !reflect.DeepEqual(session, wantSession) { + t.Errorf("begin: %v, want %v", session, wantSession) + } + if commitCount := sbc.CommitCount.Get(); commitCount != 0 { + t.Errorf("want 0, got %d", commitCount) + } + + // Begin again should cause a commit and a new begin. + session, _, err = rpcVTGate.Execute(context.Background(), "select id from t1", nil, "", topodatapb.TabletType_MASTER, session, false, nil) + if err != nil { + t.Fatal(err) + } + session, _, err = rpcVTGate.Execute(context.Background(), "begin", nil, "", topodatapb.TabletType_MASTER, session, false, nil) + if err != nil { + t.Fatal(err) + } + wantSession = &vtgatepb.Session{InTransaction: true} + if !reflect.DeepEqual(session, wantSession) { + t.Errorf("begin: %v, want %v", session, wantSession) + } + if commitCount := sbc.CommitCount.Get(); commitCount != 1 { + t.Errorf("want 1, got %d", commitCount) + } + + // commit. + session, _, err = rpcVTGate.Execute(context.Background(), "select id from t1", nil, "", topodatapb.TabletType_MASTER, session, false, nil) + if err != nil { + t.Fatal(err) + } + session, _, err = rpcVTGate.Execute(context.Background(), "commit", nil, "", topodatapb.TabletType_MASTER, session, false, nil) + if err != nil { + t.Fatal(err) + } + wantSession = &vtgatepb.Session{} + if !reflect.DeepEqual(session, wantSession) { + t.Errorf("begin: %v, want %v", session, wantSession) + } + if commitCount := sbc.CommitCount.Get(); commitCount != 2 { + t.Errorf("want 2, got %d", commitCount) + } + + // Commit again should be a no-op. + session, _, err = rpcVTGate.Execute(context.Background(), "select id from t1", nil, "", topodatapb.TabletType_MASTER, session, false, nil) + if err != nil { + t.Fatal(err) + } + session, _, err = rpcVTGate.Execute(context.Background(), "Commit", nil, "", topodatapb.TabletType_MASTER, session, false, nil) + if err != nil { + t.Fatal(err) + } + wantSession = &vtgatepb.Session{} + if !reflect.DeepEqual(session, wantSession) { + t.Errorf("begin: %v, want %v", session, wantSession) + } + if commitCount := sbc.CommitCount.Get(); commitCount != 2 { + t.Errorf("want 2, got %d", commitCount) + } + + // rollback + session, _, err = rpcVTGate.Execute(context.Background(), "begin", nil, "", topodatapb.TabletType_MASTER, session, false, nil) + if err != nil { + t.Fatal(err) + } + session, _, err = rpcVTGate.Execute(context.Background(), "select id from t1", nil, "", topodatapb.TabletType_MASTER, session, false, nil) + if err != nil { + t.Fatal(err) + } + session, _, err = rpcVTGate.Execute(context.Background(), "rollback", nil, "", topodatapb.TabletType_MASTER, session, false, nil) + if err != nil { + t.Fatal(err) + } + wantSession = &vtgatepb.Session{} + if !reflect.DeepEqual(session, wantSession) { + t.Errorf("begin: %v, want %v", session, wantSession) + } + if rollbackCount := sbc.RollbackCount.Get(); rollbackCount != 1 { + t.Errorf("want 1, got %d", rollbackCount) + } + + // rollback again should be a no-op + session, _, err = rpcVTGate.Execute(context.Background(), "select id from t1", nil, "", topodatapb.TabletType_MASTER, session, false, nil) + session, _, err = rpcVTGate.Execute(context.Background(), "RollBack", nil, "", topodatapb.TabletType_MASTER, session, false, nil) + if err != nil { + t.Fatal(err) + } + wantSession = &vtgatepb.Session{} + if !reflect.DeepEqual(session, wantSession) { + t.Errorf("begin: %v, want %v", session, wantSession) + } + if rollbackCount := sbc.RollbackCount.Get(); rollbackCount != 1 { + t.Errorf("want 1, got %d", rollbackCount) + } + + // set. + session, _, err = rpcVTGate.Execute(context.Background(), "set autocommit=1", nil, "", topodatapb.TabletType_MASTER, session, false, nil) + if err != nil { + t.Fatal(err) + } + wantSession = &vtgatepb.Session{Autocommit: true} + if !reflect.DeepEqual(session, wantSession) { + t.Errorf("begin: %v, want %v", session, wantSession) + } + session, _, err = rpcVTGate.Execute(context.Background(), "set AUTOCOMMIT = 0", nil, "", topodatapb.TabletType_MASTER, session, false, nil) + if err != nil { + t.Fatal(err) + } + wantSession = &vtgatepb.Session{} + if !reflect.DeepEqual(session, wantSession) { + t.Errorf("begin: %v, want %v", session, wantSession) + } + + // complex set + _, _, err = rpcVTGate.Execute(context.Background(), "set autocommit=1+1", nil, "", topodatapb.TabletType_MASTER, session, false, nil) + wantErr := "invalid syntax: 1 + 1" + if err == nil || err.Error() != wantErr { + t.Errorf("Execute: %v, want %s", err, wantErr) + } + + // multi-set + _, _, err = rpcVTGate.Execute(context.Background(), "set autocommit=1, a = 2", nil, "", topodatapb.TabletType_MASTER, session, false, nil) + wantErr = "too many set values: set autocommit=1, a = 2" + if err == nil || err.Error() != wantErr { + t.Errorf("Execute: %v, want %s", err, wantErr) + } + + // unsupported set + _, _, err = rpcVTGate.Execute(context.Background(), "set a = 2", nil, "", topodatapb.TabletType_MASTER, session, false, nil) + wantErr = "unsupported construct: set a = 2" + if err == nil || err.Error() != wantErr { + t.Errorf("Execute: %v, want %s", err, wantErr) + } +} + +func TestVTGateAutocommit(t *testing.T) { + createSandbox(KsTestUnsharded) + hcVTGateTest.Reset() + sbc := hcVTGateTest.AddTestTablet("aa", "1.1.1.1", 1001, KsTestUnsharded, "0", topodatapb.TabletType_MASTER, true, 1, nil) + + // autocommit = 0 + session, _, err := rpcVTGate.Execute(context.Background(), "select id from t1", nil, "", topodatapb.TabletType_MASTER, nil, false, executeOptions) + if err != nil { + t.Fatal(err) + } + wantSession := &vtgatepb.Session{} + if !reflect.DeepEqual(session, wantSession) { + t.Errorf("autocommit=0: %v, want %v", session, wantSession) + } + + // autocommit = 1 + session, _, err = rpcVTGate.Execute(context.Background(), "set autocommit=1", nil, "", topodatapb.TabletType_MASTER, session, false, executeOptions) + if err != nil { + t.Fatal(err) + } + session, _, err = rpcVTGate.Execute(context.Background(), "update t1 set id=1", nil, "", topodatapb.TabletType_MASTER, session, false, executeOptions) + if err != nil { + t.Fatal(err) + } + wantSession = &vtgatepb.Session{Autocommit: true} + if !reflect.DeepEqual(session, wantSession) { + t.Errorf("autocommit=1: %v, want %v", session, wantSession) + } + if commitCount := sbc.CommitCount.Get(); commitCount != 1 { + t.Errorf("want 1, got %d", commitCount) + } + + // autocommit = 1, "begin" + session, _, err = rpcVTGate.Execute(context.Background(), "begin", nil, "", topodatapb.TabletType_MASTER, session, false, executeOptions) + if err != nil { + t.Fatal(err) + } + session, _, err = rpcVTGate.Execute(context.Background(), "update t1 set id=1", nil, "", topodatapb.TabletType_MASTER, session, false, executeOptions) + if err != nil { + t.Fatal(err) + } + wantSession = &vtgatepb.Session{InTransaction: true, Autocommit: true} + testSession := *session + testSession.ShardSessions = nil + if !reflect.DeepEqual(&testSession, wantSession) { + t.Errorf("autocommit=1: %v, want %v", &testSession, wantSession) + } + if commitCount := sbc.CommitCount.Get(); commitCount != 1 { + t.Errorf("want 1, got %d", commitCount) + } + session, _, err = rpcVTGate.Execute(context.Background(), "commit", nil, "", topodatapb.TabletType_MASTER, session, false, executeOptions) + if err != nil { + t.Fatal(err) + } + wantSession = &vtgatepb.Session{Autocommit: true} + if !reflect.DeepEqual(session, wantSession) { + t.Errorf("autocommit=1: %v, want %v", session, wantSession) + } + if commitCount := sbc.CommitCount.Get(); commitCount != 2 { + t.Errorf("want 2, got %d", commitCount) + } +} + func TestVTGateExecuteShards(t *testing.T) { ks := "TestVTGateExecuteShards" shard := "0" @@ -634,6 +875,48 @@ func TestVTGateExecuteEntityIds(t *testing.T) { } } +func TestVTGateExecuteBatch(t *testing.T) { + createSandbox(KsTestUnsharded) + hcVTGateTest.Reset() + sbc := hcVTGateTest.AddTestTablet("aa", "1.1.1.1", 1001, KsTestUnsharded, "0", topodatapb.TabletType_MASTER, true, 1, nil) + + sqlList := []string{ + "begin", + "select id from t1", + "begin", + "select id from t1", + "commit", + "select id from t1", + "commit", + "begin", + "select id from t1", + "rollback", + "select id from t1", + "rollback", + "begin", + "select id from t1", + } + + session, qrl, err := rpcVTGate.ExecuteBatch(context.Background(), sqlList, nil, "", topodatapb.TabletType_MASTER, nil, executeOptions) + if err != nil { + t.Fatal(err) + } + // Spot-check one result. + qr := qrl[3].QueryResult + if !reflect.DeepEqual(sandboxconn.SingleRowResult, qr) { + t.Errorf("want \n%+v, got \n%+v", sandboxconn.SingleRowResult, qr) + } + if len(session.ShardSessions) != 1 { + t.Errorf("want 1, got %d", len(session.ShardSessions)) + } + if commitCount := sbc.CommitCount.Get(); commitCount != 2 { + t.Errorf("want 2, got %d", commitCount) + } + if rollbackCount := sbc.RollbackCount.Get(); rollbackCount != 1 { + t.Errorf("want 1, got %d", rollbackCount) + } +} + func TestVTGateExecuteBatchShards(t *testing.T) { ks := "TestVTGateExecuteBatchShards" createSandbox(ks) @@ -799,6 +1082,35 @@ func TestVTGateStreamExecute(t *testing.T) { } } +func TestVTGateStreamExecuteKeyspaceShard(t *testing.T) { + ks := KsTestUnsharded + shard := "0" + createSandbox(ks) + hcVTGateTest.Reset() + sbc := hcVTGateTest.AddTestTablet("aa", "1.1.1.1", 1001, ks, shard, topodatapb.TabletType_MASTER, true, 1, nil) + var qrs []*sqltypes.Result + err := rpcVTGate.StreamExecute(context.Background(), + "random statement", + nil, + ks+"/"+shard, + topodatapb.TabletType_MASTER, + executeOptions, + func(r *sqltypes.Result) error { + qrs = append(qrs, r) + return nil + }) + if err != nil { + t.Errorf("want nil, got %v", err) + } + want := []*sqltypes.Result{sandboxconn.SingleRowResult} + if !reflect.DeepEqual(want, qrs) { + t.Errorf("want \n%+v, got \n%+v", want, qrs) + } + if !proto.Equal(sbc.Options[0], executeOptions) { + t.Errorf("got ExecuteOptions \n%+v, want \n%+v", sbc.Options[0], executeOptions) + } +} + func TestVTGateStreamExecuteKeyspaceIds(t *testing.T) { ks := "TestVTGateStreamExecuteKeyspaceIds" shard1 := "-20" @@ -1559,7 +1871,7 @@ func testErrorPropagation(t *testing.T, sbcs []*sandboxconn.SandboxConn, before for _, sbc := range sbcs { before(sbc) } - _, err := rpcVTGate.Execute(context.Background(), + _, _, err := rpcVTGate.Execute(context.Background(), "select id from t1", nil, "", @@ -2021,7 +2333,7 @@ func TestErrorIssuesRollback(t *testing.T) { if err != nil { t.Fatalf("cannot start a transaction: %v", err) } - _, err = rpcVTGate.Execute(context.Background(), + session, _, err = rpcVTGate.Execute(context.Background(), "select id from t1", nil, "", @@ -2036,7 +2348,7 @@ func TestErrorIssuesRollback(t *testing.T) { t.Errorf("want 0, got %d", sbc.RollbackCount.Get()) } sbc.MustFailCodes[vtrpcpb.Code_ABORTED] = 20 - _, err = rpcVTGate.Execute(context.Background(), + session, _, err = rpcVTGate.Execute(context.Background(), "select id from t1", nil, "", @@ -2060,7 +2372,7 @@ func TestErrorIssuesRollback(t *testing.T) { if err != nil { t.Fatalf("cannot start a transaction: %v", err) } - _, err = rpcVTGate.Execute(context.Background(), + session, _, err = rpcVTGate.Execute(context.Background(), "select id from t1", nil, "", @@ -2075,7 +2387,7 @@ func TestErrorIssuesRollback(t *testing.T) { t.Errorf("want 0, got %d", sbc.RollbackCount.Get()) } sbc.MustFailCodes[vtrpcpb.Code_RESOURCE_EXHAUSTED] = 20 - _, err = rpcVTGate.Execute(context.Background(), + session, _, err = rpcVTGate.Execute(context.Background(), "select id from t1", nil, "", @@ -2099,7 +2411,7 @@ func TestErrorIssuesRollback(t *testing.T) { if err != nil { t.Fatalf("cannot start a transaction: %v", err) } - _, err = rpcVTGate.Execute(context.Background(), + session, _, err = rpcVTGate.Execute(context.Background(), "select id from t1", nil, "", @@ -2114,7 +2426,7 @@ func TestErrorIssuesRollback(t *testing.T) { t.Errorf("want 0, got %d", sbc.RollbackCount.Get()) } sbc.MustFailCodes[vtrpcpb.Code_ALREADY_EXISTS] = 20 - _, err = rpcVTGate.Execute(context.Background(), + session, _, err = rpcVTGate.Execute(context.Background(), "select id from t1", nil, "", @@ -2130,3 +2442,244 @@ func TestErrorIssuesRollback(t *testing.T) { } sbc.MustFailCodes[vtrpcpb.Code_ALREADY_EXISTS] = 0 } + +func valuesContain(rows [][]sqltypes.Value, ks string) bool { + for _, v := range rows { + if len(v) != 1 { + return false + } + if v[0].String() == ks { + return true + } + } + return false +} + +func TestVTGateShowMetadataUnsharded(t *testing.T) { + createSandbox(KsTestUnsharded) + hcVTGateTest.Reset() + hcVTGateTest.AddTestTablet("aa", "1.1.1.1", 1001, KsTestUnsharded, "0", topodatapb.TabletType_MASTER, true, 1, nil) + + _, qr, err := rpcVTGate.Execute(context.Background(), + "show databases", + nil, + "", + topodatapb.TabletType_MASTER, + nil, + false, + executeOptions) + + if err != nil { + t.Errorf("want nil, got %v", err) + } + + wantFields := []*querypb.Field{{ + Name: "Databases", + Type: sqltypes.VarChar, + }} + + if !reflect.DeepEqual(wantFields, qr.Fields) { + t.Errorf("want \n%+v, got \n%+v", wantFields, qr.Fields) + } + + if !valuesContain(qr.Rows, KsTestUnsharded) { + t.Errorf("ks %s not found in Values \n%+v", KsTestUnsharded, qr.Rows) + } + + _, qr, err = rpcVTGate.Execute(context.Background(), + "show vitess_keyspaces", + nil, + "", + topodatapb.TabletType_MASTER, + nil, + false, + executeOptions) + + if err != nil { + t.Errorf("want nil, got %v", err) + } + + wantFields = []*querypb.Field{{ + Name: "Databases", + Type: sqltypes.VarChar, + }} + + if !reflect.DeepEqual(wantFields, qr.Fields) { + t.Errorf("want \n%+v, got \n%+v", wantFields, qr.Fields) + } + + if !valuesContain(qr.Rows, KsTestUnsharded) { + t.Errorf("ks %s not found in Values \n%+v", KsTestUnsharded, qr.Rows) + } + + _, qr, err = rpcVTGate.Execute(context.Background(), + "show vitess_shards", + nil, + "", + topodatapb.TabletType_MASTER, + nil, + false, + executeOptions) + + if err != nil { + t.Errorf("want nil, got %v", err) + } + + wantFields = []*querypb.Field{{ + Name: "Shards", + Type: sqltypes.VarChar, + }} + + if !reflect.DeepEqual(wantFields, qr.Fields) { + t.Errorf("want \n%+v, got \n%+v", wantFields, qr.Fields) + } + + shard := KsTestUnsharded + "/0" + if !valuesContain(qr.Rows, shard) { + t.Errorf("shard %s not found in Values \n%+v", shard, qr.Rows) + } + + _, qr, err = rpcVTGate.Execute(context.Background(), + "show vschema_tables", + nil, + "", + topodatapb.TabletType_MASTER, + nil, + false, + executeOptions) + + expected := "vtgate: : No keyspace selected" + if err == nil || err.Error() != expected { + t.Errorf("wanted %s, got %v", expected, err) + } + + _, qr, err = rpcVTGate.Execute(context.Background(), + "show vschema_tables", + nil, + "no_such_keyspace", + topodatapb.TabletType_MASTER, + nil, + false, + executeOptions) + + expected = "vtgate: : keyspace no_such_keyspace not found in vschema" + if err == nil || err.Error() != expected { + t.Errorf("wanted %s, got %v", expected, err) + } + + _, qr, err = rpcVTGate.Execute(context.Background(), + "show vschema_tables", + nil, + KsTestUnsharded, + topodatapb.TabletType_MASTER, + nil, + false, + executeOptions) + + if err != nil { + t.Errorf("wanted nil, got %v", err) + } + + wantFields = []*querypb.Field{{ + Name: "Tables", + Type: sqltypes.VarChar, + }} + + if !reflect.DeepEqual(wantFields, qr.Fields) { + t.Errorf("want \n%+v, got \n%+v", wantFields, qr.Fields) + } + + if !valuesContain(qr.Rows, "t1") { + t.Errorf("table %s not found in Values \n%+v", "t1", qr.Rows) + } + + _, qr, err = rpcVTGate.Execute(context.Background(), + "show create databases", + nil, + "", + topodatapb.TabletType_MASTER, + nil, + false, + executeOptions) + + expected = "vtgate: : unsupported show statement" + if err == nil || err.Error() != expected { + t.Errorf("wanted %s, got %v", expected, err) + } + _, qr, err = rpcVTGate.Execute(context.Background(), + "show tables", + nil, + "", + topodatapb.TabletType_MASTER, + nil, + false, + executeOptions) + + expected = "vtgate: : unimplemented metadata query: show tables" + if err == nil || err.Error() != expected { + t.Errorf("wanted %s, got %v", expected, err) + } +} + +func TestVTGateShowMetadataTwoShards(t *testing.T) { + keyspace := "TestShowMetadataTwoShards" + setUpSandboxWithTwoShards(keyspace) + + _, qr, err := rpcVTGate.Execute(context.Background(), + "show databases", + nil, + "", + topodatapb.TabletType_MASTER, + nil, + false, + executeOptions) + + if err != nil { + t.Errorf("want nil, got %v", err) + } + + wantFields := []*querypb.Field{{ + Name: "Databases", + Type: sqltypes.VarChar, + }} + + if !reflect.DeepEqual(wantFields, qr.Fields) { + t.Errorf("want \n%+v, got \n%+v", wantFields, qr.Fields) + } + + if !valuesContain(qr.Rows, keyspace) { + t.Errorf("ks %s not found in Values \n%+v", keyspace, qr.Rows) + } + + _, qr, err = rpcVTGate.Execute(context.Background(), + "show vitess_shards", + nil, + "", + topodatapb.TabletType_MASTER, + nil, + false, + executeOptions) + + if err != nil { + t.Errorf("want nil, got %v", err) + } + + wantFields = []*querypb.Field{{ + Name: "Shards", + Type: sqltypes.VarChar, + }} + + if !reflect.DeepEqual(wantFields, qr.Fields) { + t.Errorf("want \n%+v, got \n%+v", wantFields, qr.Fields) + } + + shard0 := keyspace + "/-20" + if !valuesContain(qr.Rows, shard0) { + t.Errorf("shard %s not found in Values \n%+v", shard0, qr.Rows) + } + + shard1 := keyspace + "/20-40" + if !valuesContain(qr.Rows, shard1) { + t.Errorf("shard %s not found in Values \n%+v", shard1, qr.Rows) + } +} diff --git a/go/vt/vtgate/vtgateconntest/client.go b/go/vt/vtgate/vtgateconntest/client.go index d7103844f8c..0076f72885d 100644 --- a/go/vt/vtgate/vtgateconntest/client.go +++ b/go/vt/vtgate/vtgateconntest/client.go @@ -74,9 +74,9 @@ type queryExecute struct { } // Execute is part of the VTGateService interface -func (f *fakeVTGateService) Execute(ctx context.Context, sql string, bindVariables map[string]interface{}, keyspace string, tabletType topodatapb.TabletType, session *vtgatepb.Session, notInTransaction bool, options *querypb.ExecuteOptions) (*sqltypes.Result, error) { +func (f *fakeVTGateService) Execute(ctx context.Context, sql string, bindVariables map[string]interface{}, keyspace string, tabletType topodatapb.TabletType, session *vtgatepb.Session, notInTransaction bool, options *querypb.ExecuteOptions) (*vtgatepb.Session, *sqltypes.Result, error) { if f.hasError { - return nil, errTestVtGateError + return session, nil, errTestVtGateError } if f.panics { panic(fmt.Errorf("test forced panic")) @@ -87,7 +87,7 @@ func (f *fakeVTGateService) Execute(ctx context.Context, sql string, bindVariabl } execCase, ok := execMap[sql] if !ok { - return nil, fmt.Errorf("no match for: %s", sql) + return session, nil, fmt.Errorf("no match for: %s", sql) } query := &queryExecute{ SQL: sql, @@ -99,12 +99,12 @@ func (f *fakeVTGateService) Execute(ctx context.Context, sql string, bindVariabl } if !reflect.DeepEqual(query, execCase.execQuery) { f.t.Errorf("Execute: %+v, want %+v", query, execCase.execQuery) - return nil, nil + return session, nil, nil } if execCase.outSession != nil { *session = *execCase.outSession } - return execCase.result, nil + return session, execCase.result, nil } // queryExecuteBatch contains all the fields we use to test ExecuteBatch @@ -118,9 +118,9 @@ type queryExecuteBatch struct { } // ExecuteBatch is part of the VTGateService interface -func (f *fakeVTGateService) ExecuteBatch(ctx context.Context, sqlList []string, bindVariablesList []map[string]interface{}, keyspace string, tabletType topodatapb.TabletType, asTransaction bool, session *vtgatepb.Session, options *querypb.ExecuteOptions) ([]sqltypes.QueryResponse, error) { +func (f *fakeVTGateService) ExecuteBatch(ctx context.Context, sqlList []string, bindVariablesList []map[string]interface{}, keyspace string, tabletType topodatapb.TabletType, session *vtgatepb.Session, options *querypb.ExecuteOptions) (*vtgatepb.Session, []sqltypes.QueryResponse, error) { if f.hasError { - return nil, errTestVtGateError + return session, nil, errTestVtGateError } if f.panics { panic(fmt.Errorf("test forced panic")) @@ -131,7 +131,7 @@ func (f *fakeVTGateService) ExecuteBatch(ctx context.Context, sqlList []string, } execCase, ok := execMap[sqlList[0]] if !ok { - return nil, fmt.Errorf("no match for: %s", sqlList) + return session, nil, fmt.Errorf("no match for: %s", sqlList) } query := &queryExecuteBatch{ SQLList: sqlList, @@ -139,16 +139,15 @@ func (f *fakeVTGateService) ExecuteBatch(ctx context.Context, sqlList []string, Keyspace: keyspace, TabletType: tabletType, Session: session, - AsTransaction: asTransaction, } if !reflect.DeepEqual(query, execCase.execQuery) { f.t.Errorf("Execute: %+v, want %+v", query, execCase.execQuery) - return nil, nil + return session, nil, nil } if execCase.outSession != nil { *session = *execCase.outSession } - return []sqltypes.QueryResponse{{ + return session, []sqltypes.QueryResponse{{ QueryResult: execCase.result, QueryError: nil, }}, nil diff --git a/go/vt/vtgate/vtgateservice/interface.go b/go/vt/vtgate/vtgateservice/interface.go index b4cfa56041a..1d96fd11e3d 100644 --- a/go/vt/vtgate/vtgateservice/interface.go +++ b/go/vt/vtgate/vtgateservice/interface.go @@ -21,12 +21,12 @@ type VTGateService interface { // Regular query execution. // All these methods can change the provided session. - Execute(ctx context.Context, sql string, bindVariables map[string]interface{}, keyspace string, tabletType topodatapb.TabletType, session *vtgatepb.Session, notInTransaction bool, options *querypb.ExecuteOptions) (*sqltypes.Result, error) + Execute(ctx context.Context, sql string, bindVariables map[string]interface{}, keyspace string, tabletType topodatapb.TabletType, session *vtgatepb.Session, notInTransaction bool, options *querypb.ExecuteOptions) (*vtgatepb.Session, *sqltypes.Result, error) ExecuteShards(ctx context.Context, sql string, bindVariables map[string]interface{}, keyspace string, shards []string, tabletType topodatapb.TabletType, session *vtgatepb.Session, notInTransaction bool, options *querypb.ExecuteOptions) (*sqltypes.Result, error) ExecuteKeyspaceIds(ctx context.Context, sql string, bindVariables map[string]interface{}, keyspace string, keyspaceIds [][]byte, tabletType topodatapb.TabletType, session *vtgatepb.Session, notInTransaction bool, options *querypb.ExecuteOptions) (*sqltypes.Result, error) ExecuteKeyRanges(ctx context.Context, sql string, bindVariables map[string]interface{}, keyspace string, keyRanges []*topodatapb.KeyRange, tabletType topodatapb.TabletType, session *vtgatepb.Session, notInTransaction bool, options *querypb.ExecuteOptions) (*sqltypes.Result, error) ExecuteEntityIds(ctx context.Context, sql string, bindVariables map[string]interface{}, keyspace string, entityColumnName string, entityKeyspaceIDs []*vtgatepb.ExecuteEntityIdsRequest_EntityId, tabletType topodatapb.TabletType, session *vtgatepb.Session, notInTransaction bool, options *querypb.ExecuteOptions) (*sqltypes.Result, error) - ExecuteBatch(ctx context.Context, sqlList []string, bindVariablesList []map[string]interface{}, keyspace string, tabletType topodatapb.TabletType, asTransaction bool, session *vtgatepb.Session, options *querypb.ExecuteOptions) ([]sqltypes.QueryResponse, error) + ExecuteBatch(ctx context.Context, sqlList []string, bindVariablesList []map[string]interface{}, keyspace string, tabletType topodatapb.TabletType, session *vtgatepb.Session, options *querypb.ExecuteOptions) (*vtgatepb.Session, []sqltypes.QueryResponse, error) ExecuteBatchShards(ctx context.Context, queries []*vtgatepb.BoundShardQuery, tabletType topodatapb.TabletType, asTransaction bool, session *vtgatepb.Session, options *querypb.ExecuteOptions) ([]sqltypes.Result, error) ExecuteBatchKeyspaceIds(ctx context.Context, queries []*vtgatepb.BoundKeyspaceIdQuery, tabletType topodatapb.TabletType, asTransaction bool, session *vtgatepb.Session, options *querypb.ExecuteOptions) ([]sqltypes.Result, error) diff --git a/go/vt/vttablet/endtoend/message_test.go b/go/vt/vttablet/endtoend/message_test.go index 1b18caa0671..b4d0dc220a4 100644 --- a/go/vt/vttablet/endtoend/message_test.go +++ b/go/vt/vttablet/endtoend/message_test.go @@ -53,6 +53,15 @@ func TestMessage(t *testing.T) { t.Fatal(err) } }() + // Once the test is done, consume any left-over pending + // messages. Some could make it into the pipeline and get + // stuck forever causing vttablet shutdown to hang. + defer func() { + go func() { + for range ch { + } + }() + }() got := <-ch want := &sqltypes.Result{ Fields: []*querypb.Field{{ diff --git a/go/vt/vttablet/tabletmanager/action_agent.go b/go/vt/vttablet/tabletmanager/action_agent.go index 7472db16a2b..275dede479f 100644 --- a/go/vt/vttablet/tabletmanager/action_agent.go +++ b/go/vt/vttablet/tabletmanager/action_agent.go @@ -546,7 +546,7 @@ func (agent *ActionAgent) Start(ctx context.Context, mysqlPort, vtPort, gRPCPort // (it needs the dbname, so it has to be delayed up to here, // but it has to be before updateState below that may use it) if initUpdateStream { - us := binlog.NewUpdateStream(agent.MysqlDaemon, agent.QueryServiceControl.SchemaEngine(), agent.DBConfigs.App.DbName) + us := binlog.NewUpdateStream(agent.TopoServer, agent.initialTablet.Keyspace, agent.TabletAlias.Cell, agent.MysqlDaemon, agent.QueryServiceControl.SchemaEngine(), agent.DBConfigs.App.DbName) agent.UpdateStream = us servenv.OnRun(func() { us.RegisterService() diff --git a/go/vt/vttablet/tabletserver/planbuilder/dml.go b/go/vt/vttablet/tabletserver/planbuilder/dml.go index 343f60595f6..05fbdcb3b52 100644 --- a/go/vt/vttablet/tabletserver/planbuilder/dml.go +++ b/go/vt/vttablet/tabletserver/planbuilder/dml.go @@ -29,6 +29,11 @@ func analyzeUpdate(upd *sqlparser.Update, tables map[string]*schema.Table) (plan return nil, err } + // Store the WHERE clause as string for the hot row protection (txserializer). + buf := sqlparser.NewTrackedBuffer(nil) + buf.Myprintf("%v", upd.Where) + plan.WhereClause = buf.ParsedQuery() + if !table.HasPrimary() { log.Warningf("no primary key for table %s", tableName) plan.Reason = ReasonTableNoIndex @@ -73,6 +78,11 @@ func analyzeDelete(del *sqlparser.Delete, tables map[string]*schema.Table) (plan return nil, err } + // Store the WHERE clause as string for the hot row protection (txserializer). + buf := sqlparser.NewTrackedBuffer(nil) + buf.Myprintf("%v", del.Where) + plan.WhereClause = buf.ParsedQuery() + if !table.HasPrimary() { log.Warningf("no primary key for table %s", tableName) plan.Reason = ReasonTableNoIndex diff --git a/go/vt/vttablet/tabletserver/planbuilder/plan.go b/go/vt/vttablet/tabletserver/planbuilder/plan.go index 18a8576bc0a..b5bfc980e12 100644 --- a/go/vt/vttablet/tabletserver/planbuilder/plan.go +++ b/go/vt/vttablet/tabletserver/planbuilder/plan.go @@ -211,6 +211,10 @@ type Plan struct { // For update: set clause if pk is changing. SecondaryPKValues []interface{} + // WhereClause is set for DMLs. It is used by the hot row protection + // to serialize e.g. UPDATEs going to the same row. + WhereClause *sqlparser.ParsedQuery + // For PlanInsertSubquery: pk columns in the subquery result. SubqueryPKColumns []int @@ -259,6 +263,8 @@ func Build(sql string, tables map[string]*schema.Table) (plan *Plan, err error) return analyzeSet(stmt), nil case *sqlparser.DDL: return analyzeDDL(stmt, tables), nil + case *sqlparser.Show: + return &Plan{PlanID: PlanOther}, nil case *sqlparser.Other: return &Plan{PlanID: PlanOther}, nil } diff --git a/go/vt/vttablet/tabletserver/planbuilder/plan_test.go b/go/vt/vttablet/tabletserver/planbuilder/plan_test.go index dced9aebca9..2b0da31b51c 100644 --- a/go/vt/vttablet/tabletserver/planbuilder/plan_test.go +++ b/go/vt/vttablet/tabletserver/planbuilder/plan_test.go @@ -23,8 +23,11 @@ import ( "github.com/youtube/vitess/go/vt/vttablet/tabletserver/schema" ) -// MarshalJSON is only used for testing. -func (ep *Plan) MarshalJSON() ([]byte, error) { +// toJSON returns a JSON of the given Plan. +// Except for "TableName", it's a 1:1 copy of the fields of "Plan". +// (The JSON output is used in the tests to compare it against the data in the +// golden files e.g. data/test/tabletserver/exec_cases.txt.) +func toJSON(p *Plan) ([]byte, error) { mplan := struct { PlanID PlanType Reason ReasonType `json:",omitempty"` @@ -37,22 +40,24 @@ func (ep *Plan) MarshalJSON() ([]byte, error) { ColumnNumbers []int `json:",omitempty"` PKValues []interface{} `json:",omitempty"` SecondaryPKValues []interface{} `json:",omitempty"` + WhereClause *sqlparser.ParsedQuery `json:",omitempty"` SubqueryPKColumns []int `json:",omitempty"` MessageReloaderQuery *sqlparser.ParsedQuery `json:",omitempty"` }{ - PlanID: ep.PlanID, - Reason: ep.Reason, - TableName: ep.TableName(), - FieldQuery: ep.FieldQuery, - FullQuery: ep.FullQuery, - OuterQuery: ep.OuterQuery, - Subquery: ep.Subquery, - UpsertQuery: ep.UpsertQuery, - ColumnNumbers: ep.ColumnNumbers, - PKValues: ep.PKValues, - SecondaryPKValues: ep.SecondaryPKValues, - SubqueryPKColumns: ep.SubqueryPKColumns, - MessageReloaderQuery: ep.MessageReloaderQuery, + PlanID: p.PlanID, + Reason: p.Reason, + TableName: p.TableName(), + FieldQuery: p.FieldQuery, + FullQuery: p.FullQuery, + OuterQuery: p.OuterQuery, + Subquery: p.Subquery, + UpsertQuery: p.UpsertQuery, + ColumnNumbers: p.ColumnNumbers, + PKValues: p.PKValues, + SecondaryPKValues: p.SecondaryPKValues, + WhereClause: p.WhereClause, + SubqueryPKColumns: p.SubqueryPKColumns, + MessageReloaderQuery: p.MessageReloaderQuery, } return json.Marshal(&mplan) } @@ -65,14 +70,14 @@ func TestPlan(t *testing.T) { if err != nil { out = err.Error() } else { - bout, err := json.Marshal(plan) + bout, err := toJSON(plan) if err != nil { t.Fatalf("Error marshalling %v: %v", plan, err) } out = string(bout) } if out != tcase.output { - t.Errorf("Line:%v\n%s\n%s", tcase.lineno, tcase.output, out) + t.Errorf("Line:%v\ngot = %s\nwant = %s", tcase.lineno, out, tcase.output) if err != nil { out = fmt.Sprintf("\"%s\"", out) } else { @@ -108,14 +113,14 @@ func TestCustom(t *testing.T) { if err != nil { out = err.Error() } else { - bout, err := json.Marshal(plan) + bout, err := toJSON(plan) if err != nil { t.Fatalf("Error marshalling %v: %v", plan, err) } out = string(bout) } if out != tcase.output { - t.Errorf("File: %s: Line:%v\n%s\n%s", file, tcase.lineno, tcase.output, out) + t.Errorf("File: %s: Line:%v\ngot = %s\nwant = %s", file, tcase.lineno, out, tcase.output) } } } @@ -130,14 +135,14 @@ func TestStreamPlan(t *testing.T) { if err != nil { out = err.Error() } else { - bout, err := json.Marshal(plan) + bout, err := toJSON(plan) if err != nil { t.Fatalf("Error marshalling %v: %v", plan, err) } out = string(bout) } if out != tcase.output { - t.Errorf("Line:%v\n%s\n%s", tcase.lineno, tcase.output, out) + t.Errorf("Line:%v\ngot = %s\nwant = %s", tcase.lineno, out, tcase.output) } //fmt.Printf("%s\n%s\n\n", tcase.input, out) } diff --git a/go/vt/vttablet/tabletserver/query_engine.go b/go/vt/vttablet/tabletserver/query_engine.go index fc13c584928..827f4f9f148 100644 --- a/go/vt/vttablet/tabletserver/query_engine.go +++ b/go/vt/vttablet/tabletserver/query_engine.go @@ -27,12 +27,13 @@ import ( "github.com/youtube/vitess/go/vt/sqlparser" "github.com/youtube/vitess/go/vt/tableacl" tacl "github.com/youtube/vitess/go/vt/tableacl/acl" + "github.com/youtube/vitess/go/vt/vterrors" "github.com/youtube/vitess/go/vt/vttablet/tabletserver/connpool" - "github.com/youtube/vitess/go/vt/vttablet/tabletserver/schema" "github.com/youtube/vitess/go/vt/vttablet/tabletserver/planbuilder" "github.com/youtube/vitess/go/vt/vttablet/tabletserver/rules" + "github.com/youtube/vitess/go/vt/vttablet/tabletserver/schema" "github.com/youtube/vitess/go/vt/vttablet/tabletserver/tabletenv" - "github.com/youtube/vitess/go/vt/vterrors" + "github.com/youtube/vitess/go/vt/vttablet/tabletserver/txserializer" querypb "github.com/youtube/vitess/go/vt/proto/query" vtrpcpb "github.com/youtube/vitess/go/vt/proto/vtrpc" @@ -109,6 +110,12 @@ type QueryEngine struct { // Services consolidator *sync2.Consolidator + // txSerializer protects vttablet from applications which try to concurrently + // UPDATE (or DELETE) a "hot" row (or range of rows). + // Such queries would be serialized by MySQL anyway. This serializer prevents + // that we start more than one transaction per hot row (range). + // For implementation details, please see BeginExecute() in tabletserver.go. + txSerializer *txserializer.TxSerializer streamQList *QueryList // Vars @@ -158,6 +165,8 @@ func NewQueryEngine(checker MySQLChecker, se *schema.Engine, config tabletenv.Ta ) qe.consolidator = sync2.NewConsolidator() + qe.txSerializer = txserializer.New(config.EnableHotRowProtectionDryRun, + config.HotRowProtectionMaxQueueSize, config.HotRowProtectionMaxGlobalQueueSize) qe.streamQList = NewQueryList() qe.strictMode.Set(config.StrictMode) @@ -202,6 +211,7 @@ func NewQueryEngine(checker MySQLChecker, se *schema.Engine, config tabletenv.Ta _ = stats.NewMultiCountersFunc("QueryErrorCounts", []string{"Table", "Plan"}, qe.getQueryErrorCount) http.Handle("/debug/consolidations", qe.consolidator) + http.Handle("/debug/hotrows", qe.txSerializer) endpoints := []string{ "/debug/tablet_plans", diff --git a/go/vt/vttablet/tabletserver/splitquery/full_scan_algorithm.go b/go/vt/vttablet/tabletserver/splitquery/full_scan_algorithm.go index b2c406895ce..b9ba8b598b1 100644 --- a/go/vt/vttablet/tabletserver/splitquery/full_scan_algorithm.go +++ b/go/vt/vttablet/tabletserver/splitquery/full_scan_algorithm.go @@ -77,14 +77,11 @@ func (a *FullScanAlgorithm) generateBoundaries() ([]tuple, error) { } result := make([]tuple, 0, a.splitParams.splitCount) var iteration int64 - // We have to allow for more than splitCount query-parts since we use an estimated - // tableSize in the equation 'numRowsPerQueryPart * splitCount = tableSize'. - maxIterations := 10 * a.splitParams.splitCount + // We used to have a safety check that makes sure the number of iterations does not + // exceed 10*a.splitParams.splitCount. The splitCount parameter was calculated from + // the estimated number of rows in the information schema, which could have been grossly + // inaccurate (more than 10 times too low). for iteration = 0; prevTuple != nil; iteration++ { - if iteration > maxIterations { - panic(fmt.Sprintf("splitquery.FullScanAlgorithm.generateBoundaries(): didn't terminate"+ - " after %v iterations (=10*splitCount). FullScanAlgorithm: %v", maxIterations, a)) - } result = append(result, prevTuple) a.populatePrevTupleInBindVariables(prevTuple, a.noninitialQuery.BindVariables) prevTuple, err = a.executeQuery(a.noninitialQuery) diff --git a/go/vt/vttablet/tabletserver/tabletenv/config.go b/go/vt/vttablet/tabletserver/tabletenv/config.go index 576b4666d59..e6bb109497b 100644 --- a/go/vt/vttablet/tabletserver/tabletenv/config.go +++ b/go/vt/vttablet/tabletserver/tabletenv/config.go @@ -5,6 +5,7 @@ package tabletenv import ( + "errors" "flag" "fmt" "net/url" @@ -65,6 +66,11 @@ func init() { flag.BoolVar(&Config.EnableTxThrottler, "enable-tx-throttler", DefaultQsConfig.EnableTxThrottler, "If true replication-lag-based throttling on transactions will be enabled.") flag.StringVar(&Config.TxThrottlerConfig, "tx-throttler-config", DefaultQsConfig.TxThrottlerConfig, "The configuration of the transaction throttler as a text formatted throttlerdata.Configuration protocol buffer message") flagutil.StringListVar(&Config.TxThrottlerHealthCheckCells, "tx-throttler-healthcheck-cells", DefaultQsConfig.TxThrottlerHealthCheckCells, "A comma-separated list of cells. Only tabletservers running in these cells will be monitored for replication lag by the transaction throttler.") + + flag.BoolVar(&Config.EnableHotRowProtection, "enable_hot_row_protection", DefaultQsConfig.EnableHotRowProtection, "If true, incoming transactions for the same row (range) will be queued and cannot consume all txpool slots.") + flag.BoolVar(&Config.EnableHotRowProtectionDryRun, "enable_hot_row_protection_dry_run", DefaultQsConfig.EnableHotRowProtectionDryRun, "If true, hot row protection is not enforced but logs if transactions would have been queued.") + flag.IntVar(&Config.HotRowProtectionMaxQueueSize, "hot_row_protection_max_queue_size", DefaultQsConfig.HotRowProtectionMaxQueueSize, "Maximum number of BeginExecute RPCs which will be queued for the same row (range).") + flag.IntVar(&Config.HotRowProtectionMaxGlobalQueueSize, "hot_row_protection_max_global_queue_size", DefaultQsConfig.HotRowProtectionMaxGlobalQueueSize, "Global queue limit across all row (ranges). Useful to prevent that the queue can grow unbounded.") } // Init must be called after flag.Parse, and before doing any other operations. @@ -104,6 +110,11 @@ type TabletConfig struct { EnableTxThrottler bool TxThrottlerConfig string TxThrottlerHealthCheckCells []string + + EnableHotRowProtection bool + EnableHotRowProtectionDryRun bool + HotRowProtectionMaxQueueSize int + HotRowProtectionMaxGlobalQueueSize int } // DefaultQsConfig is the default value for the query service config. @@ -140,10 +151,15 @@ var DefaultQsConfig = TabletConfig{ TwoPCCoordinatorAddress: "", TwoPCAbandonAge: 0, - EnableTxThrottler: false, - TxThrottlerConfig: defaultTxThrottlerConfig(), - + EnableTxThrottler: false, + TxThrottlerConfig: defaultTxThrottlerConfig(), TxThrottlerHealthCheckCells: []string{}, + + EnableHotRowProtection: false, + EnableHotRowProtectionDryRun: false, + // Default value is the same as TransactionCap. + HotRowProtectionMaxQueueSize: 20, + HotRowProtectionMaxGlobalQueueSize: 1000, } // defaultTxThrottlerConfig formats the default throttlerdata.Configuration @@ -163,6 +179,23 @@ func defaultTxThrottlerConfig() string { // except for tests. var Config TabletConfig +// VerifyConfig checks "Config" for contradicting flags. +func VerifyConfig() error { + if actual, dryRun := Config.EnableHotRowProtection, Config.EnableHotRowProtectionDryRun; actual && dryRun { + return errors.New("only one of two flags allowed: -enable_hot_row_protection or -enable_hot_row_protection_dry_run") + } + if v := Config.HotRowProtectionMaxQueueSize; v <= 0 { + return fmt.Errorf("-hot_row_protection_max_queue_size must be > 0 (specified value: %v)", v) + } + if v := Config.HotRowProtectionMaxGlobalQueueSize; v <= 0 { + return fmt.Errorf("-hot_row_protection_max_global_queue_size must be > 0 (specified value: %v)", v) + } + if globalSize, size := Config.HotRowProtectionMaxGlobalQueueSize, Config.HotRowProtectionMaxQueueSize; globalSize < size { + return fmt.Errorf("global queue size must be >= per row (range) queue size: -hot_row_protection_max_global_queue_size < hot_row_protection_max_queue_size (%v < %v)", globalSize, size) + } + return nil +} + func buildFmter(logger *streamlog.StreamLogger) func(url.Values, interface{}) string { type formatter interface { Format(url.Values) string diff --git a/go/vt/vttablet/tabletserver/tabletserver.go b/go/vt/vttablet/tabletserver/tabletserver.go index df68bf1b006..ebc281aeb1e 100644 --- a/go/vt/vttablet/tabletserver/tabletserver.go +++ b/go/vt/vttablet/tabletserver/tabletserver.go @@ -17,6 +17,7 @@ import ( "golang.org/x/net/context" "github.com/youtube/vitess/go/acl" + "github.com/youtube/vitess/go/hack" "github.com/youtube/vitess/go/history" "github.com/youtube/vitess/go/mysqlconn" "github.com/youtube/vitess/go/mysqlconn/replication" @@ -37,11 +38,13 @@ import ( "github.com/youtube/vitess/go/vt/vttablet/queryservice" "github.com/youtube/vitess/go/vt/vttablet/tabletserver/connpool" "github.com/youtube/vitess/go/vt/vttablet/tabletserver/messager" + "github.com/youtube/vitess/go/vt/vttablet/tabletserver/planbuilder" "github.com/youtube/vitess/go/vt/vttablet/tabletserver/querytypes" "github.com/youtube/vitess/go/vt/vttablet/tabletserver/rules" "github.com/youtube/vitess/go/vt/vttablet/tabletserver/schema" "github.com/youtube/vitess/go/vt/vttablet/tabletserver/splitquery" "github.com/youtube/vitess/go/vt/vttablet/tabletserver/tabletenv" + "github.com/youtube/vitess/go/vt/vttablet/tabletserver/txserializer" "github.com/youtube/vitess/go/vt/vttablet/tabletserver/txthrottler" querypb "github.com/youtube/vitess/go/vt/proto/query" @@ -72,6 +75,8 @@ const ( // logTxPoolFull is for throttling txpool full messages in the log. var logTxPoolFull = logutil.NewThrottledLogger("TxPoolFull", 1*time.Minute) +var logComputeRowSerializerKey = logutil.NewThrottledLogger("ComputeRowSerializerKey", 1*time.Minute) + // stateName names every state. The number of elements must // match the number of states. Names can overlap. var stateName = []string{ @@ -84,9 +89,10 @@ var stateName = []string{ // TabletServer implements the RPC interface for the query service. type TabletServer struct { - QueryTimeout sync2.AtomicDuration - BeginTimeout sync2.AtomicDuration - TerseErrors bool + QueryTimeout sync2.AtomicDuration + BeginTimeout sync2.AtomicDuration + TerseErrors bool + enableHotRowProtection bool // mu is used to access state. The lock should only be held // for short periods. For longer periods, you have to transition @@ -170,13 +176,14 @@ func NewTabletServerWithNilTopoServer(config tabletenv.TabletConfig) *TabletServ // instance of TabletServer will expose its state variables. func NewTabletServer(config tabletenv.TabletConfig, topoServer topo.Server) *TabletServer { tsv := &TabletServer{ - QueryTimeout: sync2.NewAtomicDuration(time.Duration(config.QueryTimeout * 1e9)), - BeginTimeout: sync2.NewAtomicDuration(time.Duration(config.TxPoolTimeout * 1e9)), - TerseErrors: config.TerseErrors, - checkMySQLThrottler: sync2.NewSemaphore(1, 0), - streamHealthMap: make(map[int]chan<- *querypb.StreamHealthResponse), - history: history.New(10), - topoServer: topoServer, + QueryTimeout: sync2.NewAtomicDuration(time.Duration(config.QueryTimeout * 1e9)), + BeginTimeout: sync2.NewAtomicDuration(time.Duration(config.TxPoolTimeout * 1e9)), + TerseErrors: config.TerseErrors, + enableHotRowProtection: config.EnableHotRowProtection || config.EnableHotRowProtectionDryRun, + checkMySQLThrottler: sync2.NewSemaphore(1, 0), + streamHealthMap: make(map[int]chan<- *querypb.StreamHealthResponse), + history: history.New(10), + topoServer: topoServer, } tsv.se = schema.NewEngine(tsv, config) tsv.qe = NewQueryEngine(tsv, tsv.se, config) @@ -925,6 +932,16 @@ func (tsv *TabletServer) ExecuteBatch(ctx context.Context, target *querypb.Targe // BeginExecute combines Begin and Execute. func (tsv *TabletServer) BeginExecute(ctx context.Context, target *querypb.Target, sql string, bindVariables map[string]interface{}, options *querypb.ExecuteOptions) (*sqltypes.Result, int64, error) { + if tsv.enableHotRowProtection { + txDone, err := tsv.beginWaitForSameRangeTransactions(ctx, target, sql, bindVariables) + if err != nil { + return nil, 0, err + } + if txDone != nil { + defer txDone() + } + } + transactionID, err := tsv.Begin(ctx, target) if err != nil { return nil, 0, err @@ -934,6 +951,77 @@ func (tsv *TabletServer) BeginExecute(ctx context.Context, target *querypb.Targe return result, transactionID, err } +func (tsv *TabletServer) beginWaitForSameRangeTransactions(ctx context.Context, target *querypb.Target, sql string, bindVariables map[string]interface{}) (txserializer.DoneFunc, error) { + // Serialize the creation of new transactions *if* the first + // UPDATE or DELETE query has the same WHERE clause as a query which is + // already running in a transaction (only other BeginExecute() calls are + // considered). This avoids exhausting all txpool slots due to a hot row. + // + // Known Issue: There can be more than one transaction pool slot in use for + // the same row because the next transaction is unblocked after this + // BeginExecute() call is done and before Commit() on this transaction has + // been called. Due to the additional MySQL locking, this should result into + // two transaction pool slots per row at most. (This transaction pending on + // COMMIT, the next one waiting for MySQL in BEGIN+EXECUTE.) + var txDone txserializer.DoneFunc + + err := tsv.execRequest( + ctx, tsv.BeginTimeout.Get(), + "waitForSameRangeTransactions", "waitForSameRangeTransactions", nil, + target, true /* isTx */, false, /* allowOnShutdown */ + func(ctx context.Context, logStats *tabletenv.LogStats) error { + k, table := tsv.computeTxSerializerKey(ctx, logStats, sql, bindVariables) + if k == "" { + // Query is not subject to tx serialization/hot row protection. + return nil + } + + startTime := time.Now() + done, waited, waitErr := tsv.qe.txSerializer.Wait(ctx, k, table) + txDone = done + if waited { + tabletenv.WaitStats.Record("TxSerializer", startTime) + } + + return waitErr + }) + return txDone, err +} + +// computeTxSerializerKey returns a unique string ("key") used to determine +// whether two queries would update the same row (range). +// Additionally, it returns the table name (needed for updating stats vars). +// It returns an empty string as key if the row (range) cannot be parsed from +// the query and bind variables or the table name is empty. +func (tsv *TabletServer) computeTxSerializerKey(ctx context.Context, logStats *tabletenv.LogStats, sql string, bindVariables map[string]interface{}) (string, string) { + plan, err := tsv.qe.GetPlan(ctx, logStats, sql) + if err != nil { + logComputeRowSerializerKey.Errorf("failed to get plan for query: %v err: %v", sql, err) + return "", "" + } + + if plan.PlanID != planbuilder.PlanDMLPK && plan.PlanID != planbuilder.PlanDMLSubquery { + // Serialize only UPDATE or DELETE queries. + return "", "" + } + + tableName := plan.TableName() + if tableName.IsEmpty() { + // Do not serialize any queries without a table name. + return "", "" + } + + where, err := plan.WhereClause.GenerateQuery(bindVariables) + if err != nil { + logComputeRowSerializerKey.Errorf("failed to substitute bind vars in where clause: %v query: %v bind vars: %v", err, sql, bindVariables) + return "", "" + } + + // Example: table1 where id = 1 and sub_id = 2 + key := fmt.Sprintf("%s%s", tableName, hack.String(where)) + return key, tableName.String() +} + // BeginExecuteBatch combines Begin and ExecuteBatch. func (tsv *TabletServer) BeginExecuteBatch(ctx context.Context, target *querypb.Target, queries []querytypes.BoundQuery, asTransaction bool, options *querypb.ExecuteOptions) ([]sqltypes.Result, int64, error) { transactionID, err := tsv.Begin(ctx, target) diff --git a/go/vt/vttablet/tabletserver/tabletserver_test.go b/go/vt/vttablet/tabletserver/tabletserver_test.go index 1d1f80590a3..4e92371a6e1 100644 --- a/go/vt/vttablet/tabletserver/tabletserver_test.go +++ b/go/vt/vttablet/tabletserver/tabletserver_test.go @@ -13,6 +13,7 @@ import ( "runtime" "strconv" "strings" + "sync" "testing" "time" @@ -1454,6 +1455,348 @@ func TestExecuteBatchNestedTransaction(t *testing.T) { tsv.te.txPool.SetTimeout(10) } +func TestSerializeTransactionsSameRow(t *testing.T) { + // This test runs three transaction in parallel: + // tx1 | tx2 | tx3 + // However, tx1 and tx2 have the same WHERE clause (i.e. target the same row) + // and therefore tx2 cannot start until the first query of tx1 has finished. + // The actual execution looks like this: + // tx1 | tx3 + // tx2 + db := setUpTabletServerTest(t) + defer db.Close() + testUtils := newTestUtils() + config := testUtils.newQueryServiceConfig() + config.EnableHotRowProtection = true + // Reduce the txpool to 2 because we should never consume more than two slots. + config.TransactionCap = 2 + tsv := NewTabletServerWithNilTopoServer(config) + dbconfigs := testUtils.newDBConfigs(db) + target := querypb.Target{TabletType: topodatapb.TabletType_MASTER} + if err := tsv.StartService(target, dbconfigs, testUtils.newMysqld(&dbconfigs)); err != nil { + t.Fatalf("StartService failed: %v", err) + } + defer tsv.StopService() + countStart := tabletenv.WaitStats.Counts()["TxSerializer"] + + // Fake data. + q1 := "update test_table set name_string = 'tx1' where pk = :pk and name = :name" + q2 := "update test_table set name_string = 'tx2' where pk = :pk and name = :name" + q3 := "update test_table set name_string = 'tx3' where pk = :pk and name = :name" + // Every request needs their own bind variables to avoid data races. + bvTx1 := map[string]interface{}{ + "pk": 1, + "name": 1, + } + bvTx2 := map[string]interface{}{ + "pk": 1, + "name": 1, + } + bvTx3 := map[string]interface{}{ + "pk": 2, + "name": 1, + } + + // Make sure that tx2 and tx3 start only after tx1 is running its Execute(). + tx1Started := make(chan struct{}) + // Make sure that tx3 could finish while tx2 could not. + tx3Finished := make(chan struct{}) + + db.SetBeforeFunc("update test_table set name_string = 'tx1' where pk in (1) /* _stream test_table (pk ) (1 ); */", + func() { + close(tx1Started) + if err := waitForTxSerializationCount(tsv, "test_table where pk = 1 and name = 1", 2); err != nil { + t.Fatal(err) + } + }) + + // Run all three transactions. + ctx := context.Background() + wg := sync.WaitGroup{} + + // tx1. + wg.Add(1) + go func() { + defer wg.Done() + + _, tx1, err := tsv.BeginExecute(ctx, &target, q1, bvTx1, nil) + if err != nil { + t.Fatalf("failed to execute query: %s: %s", q1, err) + } + if err := tsv.Commit(ctx, &target, tx1); err != nil { + t.Fatalf("call TabletServer.Commit failed: %v", err) + } + }() + + // tx2. + wg.Add(1) + go func() { + defer wg.Done() + + <-tx1Started + _, tx2, err := tsv.BeginExecute(ctx, &target, q2, bvTx2, nil) + if err != nil { + t.Fatalf("failed to execute query: %s: %s", q2, err) + } + // TODO(mberlin): This should actually be in the BeforeFunc() of tx1 but + // then the test is hanging. It looks like the MySQL C client library cannot + // open a second connection while the request of the first connection is + // still pending. + <-tx3Finished + if err := tsv.Commit(ctx, &target, tx2); err != nil { + t.Fatalf("call TabletServer.Commit failed: %v", err) + } + }() + + // tx3. + wg.Add(1) + go func() { + defer wg.Done() + + <-tx1Started + _, tx3, err := tsv.BeginExecute(ctx, &target, q3, bvTx3, nil) + if err != nil { + t.Fatalf("failed to execute query: %s: %s", q3, err) + } + if err := tsv.Commit(ctx, &target, tx3); err != nil { + t.Fatalf("call TabletServer.Commit failed: %v", err) + } + close(tx3Finished) + }() + + wg.Wait() + + got, ok := tabletenv.WaitStats.Counts()["TxSerializer"] + want := countStart + 1 + if !ok || got != want { + t.Fatalf("only tx2 should have been serialized: ok? %v got: %v want: %v", ok, got, want) + } +} + +func waitForTxSerializationCount(tsv *TabletServer, key string, i int) error { + start := time.Now() + for { + got, want := tsv.qe.txSerializer.Pending(key), i + if got == want { + return nil + } + + if time.Since(start) > 10*time.Second { + return fmt.Errorf("wait for query count increase in TxSerializer timed out: got = %v, want = %v", got, want) + } + time.Sleep(1 * time.Millisecond) + } +} + +func TestSerializeTransactionsSameRow_TooManyPendingRequests(t *testing.T) { + // This test is similar to TestSerializeTransactionsSameRow, but tests only + // that there must not be too many pending BeginExecute() requests which are + // serialized. + // Since we start to queue before the transaction pool would queue, we need + // to enforce an upper limit as well to protect vttablet. + db := setUpTabletServerTest(t) + defer db.Close() + testUtils := newTestUtils() + config := testUtils.newQueryServiceConfig() + config.EnableHotRowProtection = true + config.HotRowProtectionMaxQueueSize = 1 + tsv := NewTabletServerWithNilTopoServer(config) + dbconfigs := testUtils.newDBConfigs(db) + target := querypb.Target{TabletType: topodatapb.TabletType_MASTER} + if err := tsv.StartService(target, dbconfigs, testUtils.newMysqld(&dbconfigs)); err != nil { + t.Fatalf("StartService failed: %v", err) + } + defer tsv.StopService() + countStart := tabletenv.WaitStats.Counts()["TxSerializer"] + + // Fake data. + q1 := "update test_table set name_string = 'tx1' where pk = :pk and name = :name" + q2 := "update test_table set name_string = 'tx2' where pk = :pk and name = :name" + // Every request needs their own bind variables to avoid data races. + bvTx1 := map[string]interface{}{ + "pk": 1, + "name": 1, + } + bvTx2 := map[string]interface{}{ + "pk": 1, + "name": 1, + } + + // Make sure that tx2 starts only after tx1 is running its Execute(). + tx1Started := make(chan struct{}) + // Signal when tx2 is done. + tx2Failed := make(chan struct{}) + + db.SetBeforeFunc("update test_table set name_string = 'tx1' where pk in (1) /* _stream test_table (pk ) (1 ); */", + func() { + close(tx1Started) + <-tx2Failed + }) + + // Run the two transactions. + ctx := context.Background() + wg := sync.WaitGroup{} + + // tx1. + wg.Add(1) + go func() { + defer wg.Done() + + _, tx1, err := tsv.BeginExecute(ctx, &target, q1, bvTx1, nil) + if err != nil { + t.Fatalf("failed to execute query: %s: %s", q1, err) + } + if err := tsv.Commit(ctx, &target, tx1); err != nil { + t.Fatalf("call TabletServer.Commit failed: %v", err) + } + }() + + // tx2. + wg.Add(1) + go func() { + defer wg.Done() + defer close(tx2Failed) + + <-tx1Started + _, _, err := tsv.BeginExecute(ctx, &target, q2, bvTx2, nil) + if err == nil || vterrors.Code(err) != vtrpcpb.Code_RESOURCE_EXHAUSTED || err.Error() != "hot row protection: too many queued transactions (1 >= 1) for the same row (table + WHERE clause: 'test_table where pk = 1 and name = 1')" { + t.Fatalf("tx2 should have failed because there are too many pending requests: %v", err) + } + // No commit necessary because the Begin failed. + }() + + wg.Wait() + + got, _ := tabletenv.WaitStats.Counts()["TxSerializer"] + want := countStart + 0 + if got != want { + t.Fatalf("tx2 should have failed early and not tracked as serialized: got: %v want: %v", got, want) + } +} + +func TestSerializeTransactionsSameRow_RequestCanceled(t *testing.T) { + // This test is similar to TestSerializeTransactionsSameRow, but tests only + // that a queued request unblocks itself when its context is done. + // + // tx1 and tx2 run against the same row. + // tx2 is blocked on tx1. Eventually, tx2 is canceled and its request fails. + // Only after that tx1 commits and finishes. + db := setUpTabletServerTest(t) + defer db.Close() + testUtils := newTestUtils() + config := testUtils.newQueryServiceConfig() + config.EnableHotRowProtection = true + tsv := NewTabletServerWithNilTopoServer(config) + dbconfigs := testUtils.newDBConfigs(db) + target := querypb.Target{TabletType: topodatapb.TabletType_MASTER} + if err := tsv.StartService(target, dbconfigs, testUtils.newMysqld(&dbconfigs)); err != nil { + t.Fatalf("StartService failed: %v", err) + } + defer tsv.StopService() + countStart := tabletenv.WaitStats.Counts()["TxSerializer"] + + // Fake data. + q1 := "update test_table set name_string = 'tx1' where pk = :pk and name = :name" + q2 := "update test_table set name_string = 'tx2' where pk = :pk and name = :name" + q3 := "update test_table set name_string = 'tx3' where pk = :pk and name = :name" + // Every request needs their own bind variables to avoid data races. + bvTx1 := map[string]interface{}{ + "pk": 1, + "name": 1, + } + bvTx2 := map[string]interface{}{ + "pk": 1, + "name": 1, + } + bvTx3 := map[string]interface{}{ + "pk": 1, + "name": 1, + } + + // Make sure that tx2 starts only after tx1 is running its Execute(). + tx1Started := make(chan struct{}) + // Signal when tx2 is done. + tx2Done := make(chan struct{}) + + db.SetBeforeFunc("update test_table set name_string = 'tx1' where pk in (1) /* _stream test_table (pk ) (1 ); */", + func() { + close(tx1Started) + // Keep blocking until tx2 was canceled. + <-tx2Done + }) + + // Run the two transactions. + ctx := context.Background() + wg := sync.WaitGroup{} + + // tx1. + wg.Add(1) + go func() { + defer wg.Done() + + _, tx1, err := tsv.BeginExecute(ctx, &target, q1, bvTx1, nil) + if err != nil { + t.Fatalf("failed to execute query: %s: %s", q1, err) + } + + if err := tsv.Commit(ctx, &target, tx1); err != nil { + t.Fatalf("call TabletServer.Commit failed: %v", err) + } + }() + + // tx2. + ctxTx2, cancelTx2 := context.WithCancel(ctx) + wg.Add(1) + go func() { + defer wg.Done() + defer close(tx2Done) + + // Wait until tx1 has started to make the test deterministic. + <-tx1Started + + _, _, err := tsv.BeginExecute(ctxTx2, &target, q2, bvTx2, nil) + if err == nil || vterrors.Code(err) != vtrpcpb.Code_CANCELED || err.Error() != "context canceled" { + t.Fatalf("tx2 should have failed because the context was canceled: %v", err) + } + // No commit necessary because the Begin failed. + }() + + // tx3. + wg.Add(1) + go func() { + defer wg.Done() + + // Wait until tx1 and tx2 are pending to make the test deterministic. + if err := waitForTxSerializationCount(tsv, "test_table where pk = 1 and name = 1", 2); err != nil { + t.Fatal(err) + } + + _, tx3, err := tsv.BeginExecute(ctx, &target, q3, bvTx3, nil) + if err != nil { + t.Fatalf("failed to execute query: %s: %s", q3, err) + } + + if err := tsv.Commit(ctx, &target, tx3); err != nil { + t.Fatalf("call TabletServer.Commit failed: %v", err) + } + }() + + // Wait until tx1, 2 and 3 are pending. + if err := waitForTxSerializationCount(tsv, "test_table where pk = 1 and name = 1", 3); err != nil { + t.Fatal(err) + } + // Now unblock tx2 and cancel it. + cancelTx2() + + wg.Wait() + + got, ok := tabletenv.WaitStats.Counts()["TxSerializer"] + want := countStart + 2 + if got != want { + t.Fatalf("tx2 and tx3 should have been serialized: ok? %v got: %v want: %v", ok, got, want) + } +} + func TestMessageStream(t *testing.T) { _, tsv, db := newTestTxExecutor(t) defer db.Close() @@ -1537,7 +1880,7 @@ func TestMessageAck(t *testing.T) { } _, err = tsv.MessageAck(ctx, &target, "msg", ids) - want = "query: select time_scheduled, id from msg where id in ('1', '2') and time_acked is null limit 10001 for update is not supported on fakesqldb" + want = "query: 'select time_scheduled, id from msg where id in ('1', '2') and time_acked is null limit 10001 for update' is not supported on fakesqldb" if err == nil || !strings.Contains(err.Error(), want) { t.Errorf("tsv.MessageAck(invalid): %v, want %s", err, want) } @@ -1580,7 +1923,7 @@ func TestRescheduleMessages(t *testing.T) { } _, err = tsv.PostponeMessages(ctx, &target, "msg", []string{"1", "2"}) - want = "query: select time_scheduled, id from msg where id in ('1', '2') and time_acked is null limit 10001 for update is not supported" + want = "query: 'select time_scheduled, id from msg where id in ('1', '2') and time_acked is null limit 10001 for update' is not supported" if err == nil || !strings.Contains(err.Error(), want) { t.Errorf("tsv.PostponeMessages(invalid):\n%v, want\n%s", err, want) } @@ -1623,7 +1966,7 @@ func TestPurgeMessages(t *testing.T) { } _, err = tsv.PurgeMessages(ctx, &target, "msg", 0) - want = "query: select time_scheduled, id from msg where time_scheduled < 0 and time_acked is not null limit 500 for update is not supported" + want = "query: 'select time_scheduled, id from msg where time_scheduled < 0 and time_acked is not null limit 500 for update' is not supported" if err == nil || !strings.Contains(err.Error(), want) { t.Errorf("tsv.PurgeMessages(invalid):\n%v, want\n%s", err, want) } @@ -1976,6 +2319,40 @@ func checkTabletServerState(t *testing.T, tsv *TabletServer, expectState int64) func getSupportedQueries() map[string]*sqltypes.Result { return map[string]*sqltypes.Result{ + // Queries for how row protection test (txserializer). + "update test_table set name_string = 'tx1' where pk in (1) /* _stream test_table (pk ) (1 ); */": { + RowsAffected: 1, + }, + "update test_table set name_string = 'tx2' where pk in (1) /* _stream test_table (pk ) (1 ); */": { + RowsAffected: 1, + }, + "update test_table set name_string = 'tx3' where pk in (1) /* _stream test_table (pk ) (1 ); */": { + RowsAffected: 1, + }, + // tx3, but with different primary key. + "update test_table set name_string = 'tx3' where pk in (2) /* _stream test_table (pk ) (2 ); */": { + RowsAffected: 1, + }, + // Complex WHERE clause requires SELECT of primary key first. + "select pk from test_table where pk = 1 and name = 1 limit 10001 for update": { + Fields: []*querypb.Field{ + {Type: sqltypes.Int64}, + }, + RowsAffected: 1, + Rows: [][]sqltypes.Value{{ + sqltypes.MakeString([]byte("1")), + }}, + }, + // Complex WHERE clause requires SELECT of primary key first. + "select pk from test_table where pk = 2 and name = 1 limit 10001 for update": { + Fields: []*querypb.Field{ + {Type: sqltypes.Int64}, + }, + RowsAffected: 1, + Rows: [][]sqltypes.Value{{ + sqltypes.MakeString([]byte("2")), + }}, + }, // queries for twopc sqlTurnoffBinlog: {}, fmt.Sprintf(sqlCreateSidecarDB, "`_vt`"): {}, diff --git a/go/vt/vttablet/tabletserver/txserializer/tx_serializer.go b/go/vt/vttablet/tabletserver/txserializer/tx_serializer.go new file mode 100644 index 00000000000..508bf433892 --- /dev/null +++ b/go/vt/vttablet/tabletserver/txserializer/tx_serializer.go @@ -0,0 +1,253 @@ +// Package txserializer provides the vttablet hot row protection. +// See the TxSerializer struct for details. +package txserializer + +import ( + "context" + "sync" + "time" + + "github.com/youtube/vitess/go/stats" + "github.com/youtube/vitess/go/sync2" + "github.com/youtube/vitess/go/vt/logutil" + "github.com/youtube/vitess/go/vt/vterrors" + + vtrpcpb "github.com/youtube/vitess/go/vt/proto/vtrpc" +) + +var ( + // waits stores how many times a transaction was queued because another + // transaction was already in flight for the same row (range). + // The key of the map is the table name of the query. + waits = stats.NewCounters("TxSerializerWaits") + // waitsDryRun is similar as "waits": In dry-run mode it records how many + // transactions would have been queued. + // The key of the map is the table name of the query. + waitsDryRun = stats.NewCounters("TxSerializerWaitsDryRun") + + // queueExceeded counts per table how many transactions were rejected because + // the max queue size per row (range) was exceeded. + queueExceeded = stats.NewCounters("TxSerializerQueueExceeded") + // queueExceededDryRun counts in dry-run mode how many transactions would have + // been rejected due to exceeding the max queue size per row (range). + queueExceededDryRun = stats.NewCounters("TxSerializerQueueExceededDryRun") + // globalQueueExceeded is the same as queueExceeded but for the global queue. + globalQueueExceeded = stats.NewInt("TxSerializerGlobalQueueExceeded") + globalQueueExceededDryRun = stats.NewInt("TxSerializerGlobalQueueExceededDryRun") +) + +// TxSerializer serializes incoming transactions which target the same row range +// i.e. table name and WHERE clause are identical. +// Additional transactions are queued and woken up in arrival order. +// +// This implementation has some parallels to the sync2.Consolidator class. +// However, there are many substantial differences: +// - Results are not shared between queued transactions. +// - Only one waiting transaction and not all are notified when the current one +// has finished. +// - Waiting transactions are woken up in FIFO order. +// - Waiting transactions are unblocked if their context is done. +// - Both the local queue (per row range) and global queue (whole process) are +// limited to avoid that queued transactions can consume the full capacity +// of vttablet. This is important if the capaciy is finite. For example, the +// number of RPCs in flight could be limited by the RPC subsystem. +type TxSerializer struct { + *sync2.ConsolidatorCache + + // Immutable fields. + dryRun bool + maxQueueSize int + maxGlobalQueueSize int + + log *logutil.ThrottledLogger + logDryRun *logutil.ThrottledLogger + logWaitsDryRun *logutil.ThrottledLogger + logQueueExceededDryRun *logutil.ThrottledLogger + logGlobalQueueExceededDryRun *logutil.ThrottledLogger + + mu sync.Mutex + queues map[string]*queue + globalSize int +} + +// New returns a TxSerializer object. +func New(dryRun bool, maxQueueSize, maxGlobalQueueSize int) *TxSerializer { + return &TxSerializer{ + ConsolidatorCache: sync2.NewConsolidatorCache(1000), + dryRun: dryRun, + maxQueueSize: maxQueueSize, + maxGlobalQueueSize: maxGlobalQueueSize, + log: logutil.NewThrottledLogger("HotRowProtection", 5*time.Second), + logDryRun: logutil.NewThrottledLogger("HotRowProtection DryRun", 5*time.Second), + logWaitsDryRun: logutil.NewThrottledLogger("HotRowProtection Waits DryRun", 5*time.Second), + logQueueExceededDryRun: logutil.NewThrottledLogger("HotRowProtection QueueExceeded DryRun", 5*time.Second), + logGlobalQueueExceededDryRun: logutil.NewThrottledLogger("HotRowProtection GlobalQueueExceeded DryRun", 5*time.Second), + queues: make(map[string]*queue), + } +} + +// DoneFunc is returned by Wait() and must be called by the caller. +type DoneFunc func() + +// Wait blocks if another transaction for the same range is already in flight. +// It returns when this transaction has its turn. +// "done" is != nil if err == nil and must be called once the transaction is +// done and the next waiting transaction can be unblocked. +// "waited" is true if Wait() had to wait for other transactions. +// "err" is not nil if a) the context is done or b) a queue limit was reached. +func (t *TxSerializer) Wait(ctx context.Context, key, table string) (done DoneFunc, waited bool, err error) { + t.mu.Lock() + defer t.mu.Unlock() + + waited, err = t.lockLocked(ctx, key, table) + if err != nil { + if waited { + // Waiting failed early e.g. due a canceled context and we did NOT get the + // token. Call "done" now because we don't return it to the caller. + t.unlockLocked(key, false /* returnToken */) + } + return nil, waited, err + } + return func() { t.unlock(key) }, waited, nil +} + +// lockLocked queues this transaction. It will unblock immediately if this +// transaction is the first in the queue or when it got the token (queue.lock). +// The method has the suffix "Locked" to clarify that "t.mu" must be locked. +func (t *TxSerializer) lockLocked(ctx context.Context, key, table string) (bool, error) { + q, ok := t.queues[key] + if !ok { + // First transaction in the queue i.e. we don't wait and return immediately. + t.queues[key] = newQueue(t.maxQueueSize) + t.globalSize++ + return false, nil + } + + if t.globalSize >= t.maxGlobalQueueSize { + if t.dryRun { + globalQueueExceededDryRun.Add(1) + t.logGlobalQueueExceededDryRun.Warningf("Would have rejected BeginExecute RPC because there are too many queued transactions (%d >= %d)", t.globalSize, t.maxGlobalQueueSize) + } else { + globalQueueExceeded.Add(1) + return false, vterrors.Errorf(vtrpcpb.Code_RESOURCE_EXHAUSTED, + "hot row protection: too many queued transactions (%d >= %d)", t.globalSize, t.maxGlobalQueueSize) + } + } + t.globalSize++ + + if q.size >= t.maxQueueSize { + if t.dryRun { + queueExceededDryRun.Add(table, 1) + t.logQueueExceededDryRun.Warningf("Would have rejected BeginExecute RPC because there are too many queued transactions (%d >= %d) for the same row (table + WHERE clause: '%v')", q.size, t.maxQueueSize, key) + } else { + // Decrement global queue size again because we return early. + t.globalSize-- + queueExceeded.Add(table, 1) + return false, vterrors.Errorf(vtrpcpb.Code_RESOURCE_EXHAUSTED, + "hot row protection: too many queued transactions (%d >= %d) for the same row (table + WHERE clause: '%v')", q.size, t.maxQueueSize, key) + } + } + q.size++ + q.count++ + if q.size > q.max { + q.max = q.size + } + // Publish the number of waits at /hotrows. + t.Record(key) + if q.size == 2 { + // Include first transaction in the count. (It was not recorded on purpose + // because it did not wait.) + t.Record(key) + } + + if t.dryRun { + waitsDryRun.Add(table, 1) + t.logWaitsDryRun.Warningf("Would have queued BeginExecute RPC for row (range): '%v' because another transaction to the same range is already in progress.", key) + return false, nil + } + + // Unlock before the wait and relock before returning because our caller + // Wait() hold the lock and assumes it still has it. + t.mu.Unlock() + defer t.mu.Lock() + + waits.Add(table, 1) + select { + case <-q.lock: + return true, nil + case <-ctx.Done(): + return true, ctx.Err() + } +} + +func (t *TxSerializer) unlock(key string) { + t.mu.Lock() + defer t.mu.Unlock() + + t.unlockLocked(key, true) +} + +func (t *TxSerializer) unlockLocked(key string, returnToken bool) { + q := t.queues[key] + q.size-- + t.globalSize-- + if q.size == 0 { + delete(t.queues, key) + + if q.max > 1 { + if t.dryRun { + t.logDryRun.Infof("%v simultaneous transactions (%v in total) for the same row range (%v) would have been queued.", q.max, q.count, key) + } else { + t.log.Infof("%v simultaneous transactions (%v in total) for the same row range (%v) were queued.", q.max, q.count, key) + } + } + } + + // Return token to queue. Wakes up the next queued transaction. + if !t.dryRun && returnToken { + q.lock <- struct{}{} + } +} + +// Pending returns the number of queued transactions (including the one which +// is currently in flight.) +func (t *TxSerializer) Pending(key string) int { + t.mu.Lock() + defer t.mu.Unlock() + + q, ok := t.queues[key] + if !ok { + return 0 + } + return q.size +} + +// queue reprents the local queue for a particular row (range). +// +// Note that we don't use a dedicated queue structure for all waiting +// transactions. Instead, we leverage that Go routines waiting for a channel +// are woken up in the order they are queued up. The "lock" field is said +// channel which has exactly one element, a token. All queued transactions are +// competing for this token. +type queue struct { + // NOTE: The following fields are guarded by TxSerializer.mu. + // size counts how many transactions are queued (includes the one + // transaction which is not waiting.) + size int + // count is the same as "size", but never gets decremented. + count int + // max is the max of "size", i.e. the maximum number of transactions which + // were simultaneously queued for the same row range. + max int + + lock chan struct{} +} + +func newQueue(max int) *queue { + return &queue{ + size: 1, + count: 1, + max: 1, + lock: make(chan struct{}, 1), + } +} diff --git a/go/vt/vttablet/tabletserver/txserializer/tx_serializer_test.go b/go/vt/vttablet/tabletserver/txserializer/tx_serializer_test.go new file mode 100644 index 00000000000..dc255bbe756 --- /dev/null +++ b/go/vt/vttablet/tabletserver/txserializer/tx_serializer_test.go @@ -0,0 +1,307 @@ +package txserializer + +import ( + "context" + "fmt" + "net/http" + "net/http/httptest" + "sync" + "testing" + "time" + + "github.com/youtube/vitess/go/vt/vterrors" + + vtrpcpb "github.com/youtube/vitess/go/vt/proto/vtrpc" +) + +func resetVariables() { + waitsDryRun.Reset() + queueExceededDryRun.Reset() + globalQueueExceededDryRun.Set(0) +} + +func TestTxSerializer(t *testing.T) { + resetVariables() + txs := New(false, 2, 3) + + // tx1. + done1, waited1, err1 := txs.Wait(context.Background(), "t1 where1", "t1") + if err1 != nil { + t.Fatal(err1) + } + if waited1 { + t.Fatalf("first transaction must never wait: %v", waited1) + } + + // tx2 (gets queued and must wait). + wg := sync.WaitGroup{} + wg.Add(1) + go func() { + defer wg.Done() + + done2, waited2, err2 := txs.Wait(context.Background(), "t1 where1", "t1") + if err2 != nil { + t.Fatal(err2) + } + if !waited2 { + t.Fatalf("second transaction must wait: %v", waited2) + } + if got, want := waits.Counts()["t1"], int64(1); got != want { + t.Fatalf("variable not incremented: got = %v, want = %v", got, want) + } + + done2() + }() + // Wait until tx2 is waiting before we try tx3. + if err := waitForPending(txs, "t1 where1", 2); err != nil { + t.Fatal(err) + } + + // tx3 (gets rejected because it would exceed the local queue). + _, _, err3 := txs.Wait(context.Background(), "t1 where1", "t1") + if got, want := vterrors.Code(err3), vtrpcpb.Code_RESOURCE_EXHAUSTED; got != want { + t.Fatalf("wrong error code: got = %v, want = %v", got, want) + } + if got, want := err3.Error(), "hot row protection: too many queued transactions (2 >= 2) for the same row (table + WHERE clause: 't1 where1')"; got != want { + t.Fatalf("transaction rejected with wrong error: got = %v, want = %v", got, want) + } + + done1() + // tx2 must have been unblocked. + wg.Wait() + + if txs.queues["t1 where1"] != nil { + t.Fatal("queue object was not deleted after last transaction") + } + + if err := testHTTPHandler(txs, 2); err != nil { + t.Fatal(err) + } +} + +func waitForPending(txs *TxSerializer, key string, i int) error { + start := time.Now() + for { + got, want := txs.Pending(key), i + if got == want { + return nil + } + + if time.Since(start) > 10*time.Second { + return fmt.Errorf("wait for TxSerializer.Pending() = %d timed out: got = %v, want = %v", i, got, want) + } + time.Sleep(1 * time.Millisecond) + } +} + +func testHTTPHandler(txs *TxSerializer, count int) error { + req, err := http.NewRequest("GET", "/hotrows", nil) + if err != nil { + return err + } + rr := httptest.NewRecorder() + txs.ServeHTTP(rr, req) + + if got, want := rr.Code, http.StatusOK; got != want { + return fmt.Errorf("wrong status code: got = %v, want = %v", got, want) + } + want := fmt.Sprintf(`Length: 1 +%d: t1 where1 +`, count) + if got := rr.Body.String(); got != want { + return fmt.Errorf("wrong content: got = \n%v\n want = \n%v", got, want) + } + + return nil +} + +// TestTxSerializerCancel runs 3 pending transactions. tx2 will get canceled +// and tx3 will be unblocked once tx1 is done. +func TestTxSerializerCancel(t *testing.T) { + resetVariables() + txs := New(false, 3, 3) + + // tx2 and tx3 will record their number once they're done waiting. + txDone := make(chan int) + + // tx1. + done1, waited1, err1 := txs.Wait(context.Background(), "t1 where1", "t1") + if err1 != nil { + t.Fatal(err1) + } + if waited1 { + t.Fatalf("first transaction must never wait: %v", waited1) + } + + // tx2 (gets queued and must wait). + ctx2, cancel2 := context.WithCancel(context.Background()) + wg := sync.WaitGroup{} + wg.Add(1) + go func() { + defer wg.Done() + + _, _, err2 := txs.Wait(ctx2, "t1 where1", "t1") + if err2 != context.Canceled { + t.Fatal(err2) + } + + txDone <- 2 + }() + // Wait until tx2 is waiting before we try tx3. + if err := waitForPending(txs, "t1 where1", 2); err != nil { + t.Fatal(err) + } + + // tx3 (gets queued and must wait as well). + wg.Add(1) + go func() { + defer wg.Done() + + done3, waited3, err3 := txs.Wait(context.Background(), "t1 where1", "t1") + if err3 != nil { + t.Fatal(err3) + } + if !waited3 { + t.Fatalf("third transaction must wait: %v", waited3) + } + + txDone <- 3 + + done3() + }() + // Wait until tx3 is waiting before we start to cancel tx2. + if err := waitForPending(txs, "t1 where1", 3); err != nil { + t.Fatal(err) + } + + // Cancel tx2. + cancel2() + if got := <-txDone; got != 2 { + t.Fatalf("tx2 should have been unblocked after the cancel: %v", got) + } + // Finish tx1. + done1() + // Wait for tx3. + if got := <-txDone; got != 3 { + t.Fatalf("wrong tx was unblocked after tx1: %v", got) + } + + wg.Wait() + + if txs.queues["t1 where1"] != nil { + t.Fatal("queue object was not deleted after last transaction") + } + + if err := testHTTPHandler(txs, 3); err != nil { + t.Fatal(err) + } +} + +// TestTxSerializerDryRun verifies that the dry-run mode does not serialize +// the two concurrent transactions for the same key. +func TestTxSerializerDryRun(t *testing.T) { + resetVariables() + txs := New(true, 1, 2) + + // tx1. + done1, waited1, err1 := txs.Wait(context.Background(), "t1 where1", "t1") + if err1 != nil { + t.Fatal(err1) + } + if waited1 { + t.Fatalf("first transaction must never wait: %v", waited1) + } + + // tx2 (would wait and exceed the local queue). + done2, waited2, err2 := txs.Wait(context.Background(), "t1 where1", "t1") + if err2 != nil { + t.Fatal(err2) + } + if waited2 { + t.Fatalf("second transaction must never wait in dry-run mode: %v", waited2) + } + if got, want := waitsDryRun.Counts()["t1"], int64(1); got != want { + t.Fatalf("variable not incremented: got = %v, want = %v", got, want) + } + if got, want := queueExceededDryRun.Counts()["t1"], int64(1); got != want { + t.Fatalf("variable not incremented: got = %v, want = %v", got, want) + } + + // tx3 (would wait and exceed the global queue). + done3, waited3, err3 := txs.Wait(context.Background(), "t1 where1", "t1") + if err3 != nil { + t.Fatal(err3) + } + if waited3 { + t.Fatalf("any transaction must never wait in dry-run mode: %v", waited3) + } + if got, want := waitsDryRun.Counts()["t1"], int64(2); got != want { + t.Fatalf("variable not incremented: got = %v, want = %v", got, want) + } + if got, want := globalQueueExceededDryRun.Get(), int64(1); got != want { + t.Fatalf("variable not incremented: got = %v, want = %v", got, want) + } + + if got, want := txs.Pending("t1 where1"), 3; got != want { + t.Fatalf("wrong number of pending transactions: got = %v, want = %v", got, want) + } + + done1() + done2() + done3() + + if txs.queues["t1 where1"] != nil { + t.Fatal("queue object was not deleted after last transaction") + } + + if err := testHTTPHandler(txs, 3); err != nil { + t.Fatal(err) + } +} + +// TestTxSerializerGlobalQueueOverflow shows that the global queue can exceed +// its limit without rejecting errors. This is the case when all transactions +// are the first first one for their row range. +// This is done on purpose to avoid that a too low global queue limit would +// reject transactions although they may succeed within the txpool constraints +// and RPC deadline. +func TestTxSerializerGlobalQueueOverflow(t *testing.T) { + txs := New(false, 1, 1 /* maxGlobalQueueSize */) + + // tx1. + done1, waited1, err1 := txs.Wait(context.Background(), "t1 where1", "t1") + if err1 != nil { + t.Fatal(err1) + } + if waited1 { + t.Fatalf("first transaction must never wait: %v", waited1) + } + + // tx2. + done2, waited2, err2 := txs.Wait(context.Background(), "t1 where2", "t1") + if err2 != nil { + t.Fatal(err2) + } + if waited2 { + t.Fatalf("second transaction for different row range must not wait: %v", waited2) + } + + // tx3 (same row range as tx1). + _, _, err3 := txs.Wait(context.Background(), "t1 where1", "t1") + if got, want := vterrors.Code(err3), vtrpcpb.Code_RESOURCE_EXHAUSTED; got != want { + t.Fatalf("wrong error code: got = %v, want = %v", got, want) + } + if got, want := err3.Error(), "hot row protection: too many queued transactions (2 >= 1)"; got != want { + t.Fatalf("transaction rejected with wrong error: got = %v, want = %v", got, want) + } + + done1() + done2() +} + +func TestTxSerializerPending(t *testing.T) { + txs := New(false, 1, 1) + if got, want := txs.Pending("t1 where1"), 0; got != want { + t.Fatalf("there should be no pending transaction: got = %v, want = %v", got, want) + } +} diff --git a/go/vt/worker/key_resolver.go b/go/vt/worker/key_resolver.go index 21597c6ce51..efb1d07f09a 100644 --- a/go/vt/worker/key_resolver.go +++ b/go/vt/worker/key_resolver.go @@ -12,10 +12,11 @@ import ( "github.com/youtube/vitess/go/vt/key" "github.com/youtube/vitess/go/vt/mysqlctl/tmutils" - tabletmanagerdatapb "github.com/youtube/vitess/go/vt/proto/tabletmanagerdata" - topodatapb "github.com/youtube/vitess/go/vt/proto/topodata" "github.com/youtube/vitess/go/vt/topo" "github.com/youtube/vitess/go/vt/vtgate/vindexes" + + tabletmanagerdatapb "github.com/youtube/vitess/go/vt/proto/tabletmanagerdata" + topodatapb "github.com/youtube/vitess/go/vt/proto/topodata" ) // This file defines the interface and implementations of sharding key resolvers. diff --git a/go/vt/worker/split_clone.go b/go/vt/worker/split_clone.go index 0c3a598a4a3..1fb7751e9e7 100644 --- a/go/vt/worker/split_clone.go +++ b/go/vt/worker/split_clone.go @@ -852,6 +852,16 @@ func (scw *SplitCloneWorker) clone(ctx context.Context, state StatusWorkerState) mu.Unlock() } + // NOTE: Code below this point must *not* use "return" to exit this Go routine + // early. Instead, "processError" must be called to cancel the context. This + // way all launched Go routines will terminate as well. + // (However, "return" in a new Go routine, i.e. not this one, is fine.) + // If Go routines have already been started, make sure that Wait() on the + // respective WaitGroup is called as well e.g. "destinationWaitGroup.Wait()" + // must always be reached. Waiting for the Go routines is important to avoid + // races between "defer throttler.ThreadFinished()" (must be executed first) + // and "defer scw.closeThrottlers()". Otherwise, vtworker will panic. + insertChannels := make([]chan string, len(scw.destinationShards)) destinationWaitGroup := sync.WaitGroup{} for shardIndex, si := range scw.destinationShards { @@ -884,14 +894,16 @@ func (scw *SplitCloneWorker) clone(ctx context.Context, state StatusWorkerState) keyResolver, err := scw.createKeyResolver(td) if err != nil { - return fmt.Errorf("cannot resolve sharding keys for keyspace %v: %v", scw.destinationKeyspace, err) + processError("cannot resolve sharding keys for keyspace %v: %v", scw.destinationKeyspace, err) + break } // TODO(mberlin): We're going to chunk *all* source shards based on the MIN // and MAX values of the *first* source shard. Is this going to be a problem? chunks, err := generateChunks(ctx, scw.wr, firstSourceTablet, td, scw.chunkCount, scw.minRowsPerChunk) if err != nil { - return err + processError("failed to split table into chunks: %v", err) + break } tableStatusList.setThreadCount(tableIndex, len(chunks)) diff --git a/go/vt/worker/utils_test.go b/go/vt/worker/utils_test.go index 875994dfd37..c7202b79fb4 100644 --- a/go/vt/worker/utils_test.go +++ b/go/vt/worker/utils_test.go @@ -25,7 +25,12 @@ import ( // This file contains common test helper. func runCommand(t *testing.T, wi *Instance, wr *wrangler.Wrangler, args []string) error { - worker, done, err := wi.RunCommand(context.Background(), args, wr, false /* runFromCli */) + // Limit the scope of the context e.g. to implicitly terminate stray Go + // routines. + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + + worker, done, err := wi.RunCommand(ctx, args, wr, false /* runFromCli */) if err != nil { return fmt.Errorf("Worker creation failed: %v", err) } diff --git a/go/vt/wrangler/testlib/fake_tablet.go b/go/vt/wrangler/testlib/fake_tablet.go index 4eb3f79e1a3..47d36a02f84 100644 --- a/go/vt/wrangler/testlib/fake_tablet.go +++ b/go/vt/wrangler/testlib/fake_tablet.go @@ -109,10 +109,6 @@ func NewFakeTablet(t *testing.T, wr *wrangler.Wrangler, cell string, uid uint32, t.Fatalf("uid has to be between 0 and 99: %v", uid) } mysqlPort := int32(3300 + uid) - if db != nil { - mysqlPort = int32(db.Port()) - } - tablet := &topodatapb.Tablet{ Alias: &topodatapb.TabletAlias{Cell: cell, Uid: uid}, Hostname: fmt.Sprintf("%vhost", cell), @@ -139,7 +135,7 @@ func NewFakeTablet(t *testing.T, wr *wrangler.Wrangler, cell string, uid uint32, // create a FakeMysqlDaemon with the right information by default fakeMysqlDaemon := mysqlctl.NewFakeMysqlDaemon(db) - fakeMysqlDaemon.MysqlPort = 3300 + int32(uid) + fakeMysqlDaemon.MysqlPort = mysqlPort return &FakeTablet{ Tablet: tablet, diff --git a/java/client/src/main/java/com/youtube/vitess/client/VTGateConn.java b/java/client/src/main/java/com/youtube/vitess/client/VTGateConn.java index 91fdd08f054..9836e413fc8 100644 --- a/java/client/src/main/java/com/youtube/vitess/client/VTGateConn.java +++ b/java/client/src/main/java/com/youtube/vitess/client/VTGateConn.java @@ -100,7 +100,7 @@ public SQLFuture execute(Context ctx, String query, @Nullable Map> executeBatch(Context ctx, List q Vtgate.ExecuteBatchRequest.Builder requestBuilder = Vtgate.ExecuteBatchRequest.newBuilder() .addAllQueries(checkNotNull(queries)) - .setKeyspace(keyspace) + .setKeyspaceShard(keyspace) .setTabletType(checkNotNull(tabletType)) .setAsTransaction(asTransaction) .setOptions(Query.ExecuteOptions.newBuilder() @@ -371,7 +371,7 @@ public Cursor streamExecute(Context ctx, String query, @Nullable Map StreamExecuteRequest.Builder requestBuilder = StreamExecuteRequest.newBuilder() .setQuery(Proto.bindQuery(checkNotNull(query), bindVars)) - .setKeyspace(keyspace) + .setKeyspaceShard(keyspace) .setTabletType(checkNotNull(tabletType)) .setOptions(Query.ExecuteOptions.newBuilder() .setIncludedFields(includedFields)); diff --git a/java/client/src/main/java/com/youtube/vitess/client/VTGateTx.java b/java/client/src/main/java/com/youtube/vitess/client/VTGateTx.java index 023089672ee..f416eb9a61e 100644 --- a/java/client/src/main/java/com/youtube/vitess/client/VTGateTx.java +++ b/java/client/src/main/java/com/youtube/vitess/client/VTGateTx.java @@ -81,7 +81,7 @@ public synchronized SQLFuture execute(Context ctx, String query, Map> executeBatch(Context ctx, List q Vtgate.ExecuteBatchRequest.Builder requestBuilder = Vtgate.ExecuteBatchRequest.newBuilder() .addAllQueries(checkNotNull(queries)) - .setKeyspace(keyspace) + .setKeyspaceShard(keyspace) .setTabletType(checkNotNull(tabletType)) .setSession(session) .setOptions(Query.ExecuteOptions.newBuilder() diff --git a/java/jdbc/src/main/java/com/flipkart/vitess/jdbc/FieldWithMetadata.java b/java/jdbc/src/main/java/com/flipkart/vitess/jdbc/FieldWithMetadata.java index 808af359418..060dffd5930 100644 --- a/java/jdbc/src/main/java/com/flipkart/vitess/jdbc/FieldWithMetadata.java +++ b/java/jdbc/src/main/java/com/flipkart/vitess/jdbc/FieldWithMetadata.java @@ -4,6 +4,7 @@ import com.flipkart.vitess.util.MysqlDefs; import com.flipkart.vitess.util.StringUtils; import com.flipkart.vitess.util.charset.CharsetMapping; +import com.google.common.annotations.VisibleForTesting; import com.youtube.vitess.proto.Query; import java.sql.SQLException; @@ -12,7 +13,7 @@ public class FieldWithMetadata { - private final VitessConnection connection; + private final ConnectionProperties connectionProperties; private final Query.Field field; private final Query.Type vitessType; private final boolean isImplicitTempTable; @@ -26,8 +27,8 @@ public class FieldWithMetadata { private int collationIndex; private int maxBytesPerChar; - public FieldWithMetadata(VitessConnection connection, Query.Field field) throws SQLException { - this.connection = connection; + public FieldWithMetadata(ConnectionProperties connectionProperties, Query.Field field) throws SQLException { + this.connectionProperties = connectionProperties; this.field = field; this.colFlag = field.getFlags(); this.vitessType = field.getType(); @@ -46,15 +47,15 @@ public FieldWithMetadata(VitessConnection connection, Query.Field field) throws // All of the below remapping and metadata fields require the extra // fields included when includeFields=IncludedFields.ALL - if (connection != null && connection.isIncludeAllFields()) { + if (connectionProperties != null && connectionProperties.isIncludeAllFields()) { this.isImplicitTempTable = checkForImplicitTemporaryTable(); // Re-map BLOB to 'real' blob type if (this.javaType == Types.BLOB) { boolean isFromFunction = field.getOrgTable().isEmpty(); - if (connection.getBlobsAreStrings() || (connection.getFunctionsNeverReturnBlobs() && isFromFunction)) { + if (connectionProperties.getBlobsAreStrings() || (connectionProperties.getFunctionsNeverReturnBlobs() && isFromFunction)) { this.javaType = Types.VARCHAR; } else if (collationIndex == CharsetMapping.MYSQL_COLLATION_INDEX_binary) { - if (connection.getUseBlobToStoreUTF8OutsideBMP() && shouldSetupForUtf8StringInBlob()) { + if (connectionProperties.getUseBlobToStoreUTF8OutsideBMP() && shouldSetupForUtf8StringInBlob()) { if (this.getColumnLength() == MysqlDefs.LENGTH_TINYBLOB || this.getColumnLength() == MysqlDefs.LENGTH_BLOB) { this.javaType = Types.VARCHAR; } else { @@ -76,14 +77,14 @@ public FieldWithMetadata(VitessConnection connection, Query.Field field) throws } // Re-map TINYINT(1) as bit or pseudo-boolean - if (this.javaType == Types.TINYINT && this.field.getColumnLength() == 1 && connection.getTinyInt1isBit()) { + if (this.javaType == Types.TINYINT && this.field.getColumnLength() == 1 && connectionProperties.getTinyInt1isBit()) { this.javaType = Types.BIT; } if (!isNativeNumericType() && !isNativeDateTimeType()) { // For non-numeric types, try to pull the encoding from the passed collationIndex // We will do some fixup afterwards - this.encoding = connection.getEncodingForIndex(this.collationIndex); + this.encoding = getEncodingForIndex(this.collationIndex); // ucs2, utf16, and utf32 cannot be used as a client character set, but if it was received from server // under some circumstances we can parse them as utf16 if ("UnicodeBig".equals(this.encoding)) { @@ -182,24 +183,37 @@ private boolean isNativeDateTimeType() { } } - public VitessConnection getConnection() throws SQLException { - checkConnection(); - return connection; + @VisibleForTesting + String getEncodingForIndex(int charsetIndex) { + String javaEncoding = null; + if (charsetIndex != MysqlDefs.NO_CHARSET_INFO) { + javaEncoding = CharsetMapping.getJavaEncodingForCollationIndex(charsetIndex, connectionProperties.getEncoding()); + } + // If nothing, get default based on configuration, may still be null + if (javaEncoding == null) { + javaEncoding = connectionProperties.getEncoding(); + } + return javaEncoding; + } + + public ConnectionProperties getConnectionProperties() throws SQLException { + checkConnectionProperties(); + return connectionProperties; } - public boolean hasConnection() { - return connection != null; + public boolean hasConnectionProperties() { + return connectionProperties != null; } - private void checkConnection() throws SQLException { - if (!hasConnection()) { + private void checkConnectionProperties() throws SQLException { + if (!hasConnectionProperties()) { throw new SQLException(Constants.SQLExceptionMessages.CONN_UNAVAILABLE); } } private boolean shouldSetupForUtf8StringInBlob() throws SQLException { - String includePattern = connection.getUtf8OutsideBmpIncludedColumnNamePattern(); - String excludePattern = connection.getUtf8OutsideBmpExcludedColumnNamePattern(); + String includePattern = connectionProperties.getUtf8OutsideBmpIncludedColumnNamePattern(); + String excludePattern = connectionProperties.getUtf8OutsideBmpExcludedColumnNamePattern(); // When UseBlobToStoreUTF8OutsideBMP is set, we by default set blobs to UTF-8. So we first // look for fields to exclude from that remapping (blacklist) @@ -228,85 +242,85 @@ private boolean shouldSetupForUtf8StringInBlob() throws SQLException { } public boolean isAutoIncrement() throws SQLException { - checkConnection(); - if (!connection.isIncludeAllFields()) { + checkConnectionProperties(); + if (!connectionProperties.isIncludeAllFields()) { return false; } return ((this.colFlag & Query.MySqlFlag.AUTO_INCREMENT_FLAG_VALUE) > 0); } public boolean isBinary() throws SQLException { - checkConnection(); - if (!connection.isIncludeAllFields()) { + checkConnectionProperties(); + if (!connectionProperties.isIncludeAllFields()) { return false; } return ((this.colFlag & Query.MySqlFlag.BINARY_FLAG_VALUE) > 0); } public boolean isBlob() throws SQLException { - checkConnection(); - if (!connection.isIncludeAllFields()) { + checkConnectionProperties(); + if (!connectionProperties.isIncludeAllFields()) { return false; } return ((this.colFlag & Query.MySqlFlag.BLOB_FLAG_VALUE) > 0); } public boolean isMultipleKey() throws SQLException { - checkConnection(); - if (!connection.isIncludeAllFields()) { + checkConnectionProperties(); + if (!connectionProperties.isIncludeAllFields()) { return false; } return ((this.colFlag & Query.MySqlFlag.MULTIPLE_KEY_FLAG_VALUE) > 0); } boolean isNotNull() throws SQLException { - checkConnection(); - if (!connection.isIncludeAllFields()) { + checkConnectionProperties(); + if (!connectionProperties.isIncludeAllFields()) { return true; } return ((this.colFlag & Query.MySqlFlag.NOT_NULL_FLAG_VALUE) > 0); } public boolean isZeroFill() throws SQLException { - checkConnection(); - if (!connection.isIncludeAllFields()) { + checkConnectionProperties(); + if (!connectionProperties.isIncludeAllFields()) { return false; } return ((this.colFlag & Query.MySqlFlag.ZEROFILL_FLAG_VALUE) > 0); } public boolean isPrimaryKey() throws SQLException { - checkConnection(); - if (!connection.isIncludeAllFields()) { + checkConnectionProperties(); + if (!connectionProperties.isIncludeAllFields()) { return false; } return ((this.colFlag & Query.MySqlFlag.PRI_KEY_FLAG_VALUE) > 0); } public boolean isUniqueKey() throws SQLException { - checkConnection(); - if (!connection.isIncludeAllFields()) { + checkConnectionProperties(); + if (!connectionProperties.isIncludeAllFields()) { return false; } return ((this.colFlag & Query.MySqlFlag.UNIQUE_KEY_FLAG_VALUE) > 0); } public boolean isUnsigned() throws SQLException { - checkConnection(); - if (!connection.isIncludeAllFields()) { + checkConnectionProperties(); + if (!connectionProperties.isIncludeAllFields()) { return true; } return ((this.colFlag & Query.MySqlFlag.UNSIGNED_FLAG_VALUE) > 0); } public boolean isSigned() throws SQLException { - checkConnection(); + checkConnectionProperties(); return !isUnsigned(); } boolean isOpaqueBinary() throws SQLException { - checkConnection(); - if (!connection.isIncludeAllFields()) { + checkConnectionProperties(); + if (!connectionProperties.isIncludeAllFields()) { return false; } @@ -329,8 +343,8 @@ boolean isOpaqueBinary() throws SQLException { * statement. */ boolean isReadOnly() throws SQLException { - checkConnection(); - if (!connection.isIncludeAllFields()) { + checkConnectionProperties(); + if (!connectionProperties.isIncludeAllFields()) { return false; } String orgColumnName = getOrgName(); @@ -339,7 +353,7 @@ boolean isReadOnly() throws SQLException { } public synchronized String getCollation() throws SQLException { - if (!connection.isIncludeAllFields()) { + if (!connectionProperties.isIncludeAllFields()) { return null; } @@ -356,63 +370,75 @@ public synchronized String getCollation() throws SQLException { public synchronized int getMaxBytesPerCharacter() { - if (!connection.isIncludeAllFields()) { + if (!connectionProperties.isIncludeAllFields()) { return 0; } if (this.maxBytesPerChar == 0) { - this.maxBytesPerChar = this.connection.getMaxBytesPerChar(getCollationIndex(), getEncoding()); + this.maxBytesPerChar = getMaxBytesPerChar(getCollationIndex(), getEncoding()); } return this.maxBytesPerChar; } + @VisibleForTesting + int getMaxBytesPerChar(Integer charsetIndex, String javaCharsetName) { + // if we can get it by charsetIndex just doing it + String charset = CharsetMapping.getMysqlCharsetNameForCollationIndex(charsetIndex); + // if we didn't find charset name by its full name + if (charset == null) { + charset = CharsetMapping.getMysqlCharsetForJavaEncoding(javaCharsetName); + } + // checking against static maps + return CharsetMapping.getMblen(charset); + } + public String getName() { return field.getName(); } public String getTable() throws SQLException { - checkConnection(); - if (!connection.isIncludeAllFields()) { + checkConnectionProperties(); + if (!connectionProperties.isIncludeAllFields()) { return null; } return field.getTable(); } public String getOrgTable() throws SQLException { - checkConnection(); - if (!connection.isIncludeAllFields()) { + checkConnectionProperties(); + if (!connectionProperties.isIncludeAllFields()) { return null; } return field.getOrgTable(); } public String getDatabase() throws SQLException { - checkConnection(); - if (!connection.isIncludeAllFields()) { + checkConnectionProperties(); + if (!connectionProperties.isIncludeAllFields()) { return null; } return field.getDatabase(); } public String getOrgName() throws SQLException { - checkConnection(); - if (!connection.isIncludeAllFields()) { + checkConnectionProperties(); + if (!connectionProperties.isIncludeAllFields()) { return null; } return field.getOrgName(); } public int getColumnLength() throws SQLException { - checkConnection(); - if (!connection.isIncludeAllFields()) { + checkConnectionProperties(); + if (!connectionProperties.isIncludeAllFields()) { return 0; } return field.getColumnLength(); } public int getDecimals() throws SQLException { - checkConnection(); - if (!connection.isIncludeAllFields()) { + checkConnectionProperties(); + if (!connectionProperties.isIncludeAllFields()) { return 0; } return field.getDecimals(); @@ -431,14 +457,14 @@ public int getVitessTypeValue() { } boolean isImplicitTemporaryTable() { - if (!connection.isIncludeAllFields()) { + if (!connectionProperties.isIncludeAllFields()) { return false; } return isImplicitTempTable; } public String getEncoding() { - if (!connection.isIncludeAllFields()) { + if (!connectionProperties.isIncludeAllFields()) { return null; } return encoding; @@ -450,16 +476,16 @@ public String getEncoding() { * numeric types */ public int getPrecisionAdjustFactor() throws SQLException { - checkConnection(); - if (!connection.isIncludeAllFields()) { + checkConnectionProperties(); + if (!connectionProperties.isIncludeAllFields()) { return 0; } return precisionAdjustFactor; } public boolean isSingleBit() throws SQLException { - checkConnection(); - if (!connection.isIncludeAllFields()) { + checkConnectionProperties(); + if (!connectionProperties.isIncludeAllFields()) { return false; } return isSingleBit; diff --git a/java/jdbc/src/main/java/com/flipkart/vitess/jdbc/VitessConnection.java b/java/jdbc/src/main/java/com/flipkart/vitess/jdbc/VitessConnection.java index 9f2814f11a7..f00c08ceb21 100644 --- a/java/jdbc/src/main/java/com/flipkart/vitess/jdbc/VitessConnection.java +++ b/java/jdbc/src/main/java/com/flipkart/vitess/jdbc/VitessConnection.java @@ -3,7 +3,6 @@ import com.flipkart.vitess.util.CommonUtils; import com.flipkart.vitess.util.Constants; import com.flipkart.vitess.util.MysqlDefs; -import com.flipkart.vitess.util.charset.CharsetMapping; import com.youtube.vitess.client.Context; import com.youtube.vitess.client.VTGateConn; import com.youtube.vitess.client.VTGateTx; @@ -215,11 +214,11 @@ public boolean isClosed() throws SQLException { public DatabaseMetaData getMetaData() throws SQLException { checkOpen(); - if (null != databaseMetaData) { + if (!metadataNullOrClosed()) { return databaseMetaData; } else { synchronized (VitessConnection.class) { - if (null == databaseMetaData) { + if (metadataNullOrClosed()) { String dbEngine = initializeDBProperties(); if (dbEngine.equals("mariadb")) { databaseMetaData = new VitessMariaDBDatabaseMetadata(this); @@ -232,6 +231,10 @@ public DatabaseMetaData getMetaData() throws SQLException { } } + private boolean metadataNullOrClosed() throws SQLException { + return null == databaseMetaData || null == databaseMetaData.getConnection() || databaseMetaData.getConnection().isClosed(); + } + public boolean isReadOnly() throws SQLException { checkOpen(); return readOnly; @@ -786,7 +789,7 @@ private String initializeDBProperties() throws SQLException { HashMap dbVariables = new HashMap<>(); String dbEngine = null; - if (null == databaseMetaData) { + if (metadataNullOrClosed()) { String versionValue; ResultSet resultSet = null; VitessStatement vitessStatement = new VitessStatement(this); @@ -846,27 +849,4 @@ public Context createContext(long deadlineAfter) { public String getUsername() { return this.vitessJDBCUrl.getUsername(); } - - public String getEncodingForIndex(int charsetIndex) { - String javaEncoding = null; - if (charsetIndex != MysqlDefs.NO_CHARSET_INFO) { - javaEncoding = CharsetMapping.getJavaEncodingForCollationIndex(charsetIndex, getEncoding()); - } - // If nothing, get default based on configuration, may still be null - if (javaEncoding == null) { - javaEncoding = getEncoding(); - } - return javaEncoding; - } - - public int getMaxBytesPerChar(Integer charsetIndex, String javaCharsetName) { - // if we can get it by charsetIndex just doing it - String charset = CharsetMapping.getMysqlCharsetNameForCollationIndex(charsetIndex); - // if we didn't find charset name by its full name - if (charset == null) { - charset = CharsetMapping.getMysqlCharsetForJavaEncoding(javaCharsetName); - } - // checking against static maps - return CharsetMapping.getMblen(charset); - } } diff --git a/java/jdbc/src/main/java/com/flipkart/vitess/jdbc/VitessMariaDBDatabaseMetadata.java b/java/jdbc/src/main/java/com/flipkart/vitess/jdbc/VitessMariaDBDatabaseMetadata.java index 5420a3567fe..fda293436e7 100644 --- a/java/jdbc/src/main/java/com/flipkart/vitess/jdbc/VitessMariaDBDatabaseMetadata.java +++ b/java/jdbc/src/main/java/com/flipkart/vitess/jdbc/VitessMariaDBDatabaseMetadata.java @@ -418,7 +418,7 @@ public ResultSet getTypeInfo() throws SQLException { {"TIMESTAMP", "93", "27", "'", "'", "[(M)]", "1", "0", "3", "0", "0", "0", "TIMESTAMP", "0", "0", "0", "0", "10"}}; - return new VitessResultSet(columnNames, columnTypes, data); + return new VitessResultSet(columnNames, columnTypes, data, this.connection); } public ResultSet getIndexInfo(String catalog, String schema, String table, boolean unique, diff --git a/java/jdbc/src/main/java/com/flipkart/vitess/jdbc/VitessMySQLDatabaseMetadata.java b/java/jdbc/src/main/java/com/flipkart/vitess/jdbc/VitessMySQLDatabaseMetadata.java index 06333fe7d4a..6f149154fad 100644 --- a/java/jdbc/src/main/java/com/flipkart/vitess/jdbc/VitessMySQLDatabaseMetadata.java +++ b/java/jdbc/src/main/java/com/flipkart/vitess/jdbc/VitessMySQLDatabaseMetadata.java @@ -454,13 +454,13 @@ public boolean supportsTransactionIsolationLevel(int level) throws SQLException Query.Type.VARCHAR, Query.Type.VARCHAR, Query.Type.VARCHAR, Query.Type.VARCHAR, Query.Type.VARCHAR, Query.Type.VARCHAR, Query.Type.VARCHAR}; - return new VitessResultSet(columnNames, columnTypes, data); + return new VitessResultSet(columnNames, columnTypes, data, this.connection); } public ResultSet getSchemas() throws SQLException { String[] columnNames = {"TABLE_SCHEM", "TABLE_CATALOG"}; Query.Type[] columnType = {Query.Type.CHAR, Query.Type.CHAR}; - return new VitessResultSet(columnNames, columnType, new String[][] {}); + return new VitessResultSet(columnNames, columnType, new String[][] {}, this.connection); } public ResultSet getCatalogs() throws SQLException { @@ -487,7 +487,7 @@ public ResultSet getCatalogs() throws SQLException { vitessStatement.close(); String[] columnName = new String[] {"TABLE_CAT"}; Query.Type[] columntype = new Query.Type[] {Query.Type.CHAR}; - return new VitessResultSet(columnName, columntype, data); + return new VitessResultSet(columnName, columntype, data, this.connection); } public ResultSet getTableTypes() throws SQLException { @@ -496,7 +496,7 @@ public ResultSet getTableTypes() throws SQLException { String[][] data = new String[][] {{"LOCAL TEMPORARY"}, {"SYSTEM TABLES"}, {"SYSTEM VIEW"}, {"TABLE"}, {"VIEW"}}; - return new VitessResultSet(columnNames, columnType, data); + return new VitessResultSet(columnNames, columnType, data, this.connection); } @SuppressWarnings("StringBufferReplaceableByString") public ResultSet getColumns(String catalog, @@ -700,7 +700,7 @@ public ResultSet getTableTypes() throws SQLException { Query.Type.INT32, Query.Type.CHAR, Query.Type.CHAR, Query.Type.CHAR, Query.Type.CHAR, Query.Type.INT16, Query.Type.CHAR, Query.Type.CHAR}; - return new VitessResultSet(columnNames, columnType, data); + return new VitessResultSet(columnNames, columnType, data, this.connection); } public ResultSet getColumnPrivileges(String catalog, String schema, String table, @@ -791,7 +791,7 @@ public ResultSet getTablePrivileges(String catalog, String schemaPattern, } vitessStatement.close(); } - return new VitessResultSet(columnName, columnType, data); + return new VitessResultSet(columnName, columnType, data, this.connection); } public ResultSet getVersionColumns(String catalog, String schema, String table) @@ -846,7 +846,7 @@ public ResultSet getVersionColumns(String catalog, String schema, String table) Query.Type[] columnType = new Query.Type[] {Query.Type.INT16, Query.Type.CHAR, Query.Type.INT32, Query.Type.CHAR, Query.Type.INT32, Query.Type.INT32, Query.Type.INT16, Query.Type.INT16}; - return new VitessResultSet(columnNames, columnType, data); + return new VitessResultSet(columnNames, columnType, data, this.connection); } @SuppressWarnings("StringBufferReplaceableByString") public ResultSet getPrimaryKeys( @@ -902,7 +902,7 @@ public ResultSet getVersionColumns(String catalog, String schema, String table) new Query.Type[] {Query.Type.CHAR, Query.Type.CHAR, Query.Type.CHAR, Query.Type.CHAR, Query.Type.INT16, Query.Type.CHAR}; - return new VitessResultSet(columnNames, columnType, sortedData); + return new VitessResultSet(columnNames, columnType, sortedData, this.connection); } public ResultSet getImportedKeys(String catalog, String schema, String table) @@ -1019,7 +1019,7 @@ public ResultSet getTypeInfo() throws SQLException { {"TIMESTAMP", "93", "0", "'", "'", "[(M)]", "1", "false", "3", "false", "false", "false", "TIMESTAMP", "0", "0", "0", "0", "10"}}; - return new VitessResultSet(columnNames, columnTypes, data); + return new VitessResultSet(columnNames, columnTypes, data, this.connection); } @SuppressWarnings("StringBufferReplaceableByString") public ResultSet getIndexInfo( @@ -1088,7 +1088,7 @@ public ResultSet getTypeInfo() throws SQLException { Query.Type.CHAR, Query.Type.CHAR, Query.Type.INT32, Query.Type.INT32, Query.Type.CHAR}; - return new VitessResultSet(columnName, columnType, data); + return new VitessResultSet(columnName, columnType, data, this.connection); } public boolean ownUpdatesAreVisible(int type) throws SQLException { @@ -1131,7 +1131,7 @@ public ResultSet getUDTs(String catalog, String schemaPattern, String typeNamePa Query.Type[] columnType = {Query.Type.VARCHAR, Query.Type.INT32, Query.Type.VARCHAR, Query.Type.VARCHAR, Query.Type.INT32, Query.Type.VARCHAR, Query.Type.INT16}; - return new VitessResultSet(columnNames, columnType, new String[][] {}); + return new VitessResultSet(columnNames, columnType, new String[][] {}, this.connection); } public ResultSet getSuperTypes(String catalog, String schemaPattern, String typeNamePattern) @@ -1142,7 +1142,7 @@ public ResultSet getSuperTypes(String catalog, String schemaPattern, String type Query.Type[] columnType = {Query.Type.CHAR, Query.Type.CHAR, Query.Type.CHAR, Query.Type.CHAR, Query.Type.CHAR, Query.Type.CHAR}; - return new VitessResultSet(columnNames, columnType, new String[][] {}); + return new VitessResultSet(columnNames, columnType, new String[][] {}, this.connection); } public ResultSet getSuperTables(String catalog, String schemaPattern, String tableNamePattern) @@ -1150,7 +1150,7 @@ public ResultSet getSuperTables(String catalog, String schemaPattern, String tab String[] columnNames = {"TABLE_CAT", "TYPE_SCHEM", "TABLE_NAME", "SUPERTABLE_NAME"}; Query.Type[] columnType = {Query.Type.CHAR, Query.Type.CHAR, Query.Type.CHAR, Query.Type.CHAR}; - return new VitessResultSet(columnNames, columnType, new String[][] {}); + return new VitessResultSet(columnNames, columnType, new String[][] {}, this.connection); } public ResultSet getAttributes(String catalog, String schemaPattern, String typeNamePattern, @@ -1167,7 +1167,7 @@ public ResultSet getAttributes(String catalog, String schemaPattern, String type Query.Type.INT32, Query.Type.CHAR, Query.Type.CHAR, Query.Type.INT32, Query.Type.INT32, Query.Type.INT32, Query.Type.INT32, Query.Type.CHAR, Query.Type.CHAR, Query.Type.CHAR, Query.Type.CHAR, Query.Type.INT16}; - return new VitessResultSet(columnNames, columnType, new String[][] {}); + return new VitessResultSet(columnNames, columnType, new String[][] {}, this.connection); } public int getSQLStateType() throws SQLException { @@ -1186,14 +1186,14 @@ public RowIdLifetime getRowIdLifetime() throws SQLException { public ResultSet getSchemas(String catalog, String schemaPattern) throws SQLException { String[] columnNames = {"TABLE_CAT", "TABLE_CATALOG"}; Query.Type[] columnType = {Query.Type.CHAR, Query.Type.CHAR}; - return new VitessResultSet(columnNames, columnType, new String[][] {}); + return new VitessResultSet(columnNames, columnType, new String[][] {}, this.connection); } public ResultSet getClientInfoProperties() throws SQLException { String[] columnNames = {"NAME", "MAX_LEN", "DEFAULT_VALUE", "DESCRIPTION"}; Query.Type[] columnType = {Query.Type.VARCHAR, Query.Type.INT32, Query.Type.VARCHAR, Query.Type.VARCHAR}; - return new VitessResultSet(columnNames, columnType, new String[][] {}); + return new VitessResultSet(columnNames, columnType, new String[][] {}, this.connection); } public ResultSet getFunctions(String catalog, String schemaPattern, String functionNamePattern) @@ -1218,7 +1218,7 @@ public ResultSet getPseudoColumns(String catalog, String schemaPattern, String t {Query.Type.VARCHAR, Query.Type.VARCHAR, Query.Type.VARCHAR, Query.Type.VARCHAR, Query.Type.INT32, Query.Type.INT32, Query.Type.INT32, Query.Type.INT32, Query.Type.VARCHAR, Query.Type.VARCHAR, Query.Type.INT32, Query.Type.VARCHAR}; - return new VitessResultSet(columnNames, columnType, new String[][] {}); + return new VitessResultSet(columnNames, columnType, new String[][] {}, this.connection); } public T unwrap(Class iface) throws SQLException { diff --git a/java/jdbc/src/main/java/com/flipkart/vitess/jdbc/VitessResultSet.java b/java/jdbc/src/main/java/com/flipkart/vitess/jdbc/VitessResultSet.java index 81f8f69fed7..288a4ccfd0f 100644 --- a/java/jdbc/src/main/java/com/flipkart/vitess/jdbc/VitessResultSet.java +++ b/java/jdbc/src/main/java/com/flipkart/vitess/jdbc/VitessResultSet.java @@ -71,7 +71,7 @@ public VitessResultSet(Cursor cursor, VitessStatement vitessStatement) throws SQ this.cursor = cursor; this.vitessStatement = vitessStatement; try { - this.fields = enhancedFieldsFromCursor(); + this.fields = enhancedFieldsFromCursor(vitessStatement == null ? null : vitessStatement.getConnection()); } catch (SQLException e) { throw new SQLException(Constants.SQLExceptionMessages.RESULT_SET_INIT_ERROR, e); } @@ -82,7 +82,7 @@ public VitessResultSet(Cursor cursor, VitessStatement vitessStatement) throws SQ } } - public VitessResultSet(String[] columnNames, Query.Type[] columnTypes, String[][] data) + public VitessResultSet(String[] columnNames, Query.Type[] columnTypes, String[][] data, ConnectionProperties connection) throws SQLException { if (columnNames.length != columnTypes.length) { @@ -112,7 +112,7 @@ public VitessResultSet(String[] columnNames, Query.Type[] columnTypes, String[][ this.cursor = new SimpleCursor(queryResultBuilder.build()); this.vitessStatement = null; try { - this.fields = enhancedFieldsFromCursor(); + this.fields = enhancedFieldsFromCursor(connection); } catch (SQLException e) { throw new SQLException(Constants.SQLExceptionMessages.RESULT_SET_INIT_ERROR, e); } @@ -120,7 +120,7 @@ public VitessResultSet(String[] columnNames, Query.Type[] columnTypes, String[][ } public VitessResultSet(String[] columnNames, Query.Type[] columnTypes, - ArrayList> data) throws SQLException { + ArrayList> data, VitessConnection connection) throws SQLException { if (columnNames.length != columnTypes.length) { throw new SQLException(Constants.SQLExceptionMessages.INVALID_RESULT_SET); @@ -151,18 +151,17 @@ public VitessResultSet(String[] columnNames, Query.Type[] columnTypes, this.cursor = new SimpleCursor(queryResultBuilder.build()); this.vitessStatement = null; try { - this.fields = enhancedFieldsFromCursor(); + this.fields = enhancedFieldsFromCursor(connection); } catch (SQLException e) { throw new SQLException(Constants.SQLExceptionMessages.RESULT_SET_INIT_ERROR, e); } this.currentRow = 0; } - private List enhancedFieldsFromCursor() throws SQLException { + private List enhancedFieldsFromCursor(ConnectionProperties connection) throws SQLException { if (cursor == null|| cursor.getFields() == null) { throw new SQLException(Constants.SQLExceptionMessages.CURSOR_NULL); } - VitessConnection connection = vitessStatement == null ? null : vitessStatement.getConnection(); List rawFields = cursor.getFields(); List fields = new ArrayList<>(rawFields.size()); for (Query.Field field : rawFields) { @@ -222,7 +221,7 @@ public String getString(int columnIndex) throws SQLException { object = this.row.getObject(columnIndex); if (object instanceof byte[]) { FieldWithMetadata field = this.fields.get(columnIndex - 1); - if (field.hasConnection() && field.getConnection().isIncludeAllFields()) { + if (field.hasConnectionProperties() && field.getConnectionProperties().isIncludeAllFields()) { columnValue = convertBytesToString((byte[]) object, field.getEncoding()); } else { columnValue = new String((byte[]) object); @@ -558,7 +557,7 @@ public Object getObject(int columnIndex) throws SQLException { Object retVal = this.row.getObject(columnIndex); FieldWithMetadata field = this.fields.get(columnIndex - 1); - if (field.hasConnection() && field.getConnection().isIncludeAllFields() && retVal instanceof byte[]) { + if (field.hasConnectionProperties() && field.getConnectionProperties().isIncludeAllFields() && retVal instanceof byte[]) { retVal = convertBytesIfPossible((byte[]) retVal, field); } diff --git a/java/jdbc/src/main/java/com/flipkart/vitess/jdbc/VitessResultSetMetaData.java b/java/jdbc/src/main/java/com/flipkart/vitess/jdbc/VitessResultSetMetaData.java index 8355c222f64..eb02fbfddde 100644 --- a/java/jdbc/src/main/java/com/flipkart/vitess/jdbc/VitessResultSetMetaData.java +++ b/java/jdbc/src/main/java/com/flipkart/vitess/jdbc/VitessResultSetMetaData.java @@ -52,7 +52,7 @@ public boolean isCaseSensitive(int column) throws SQLException { case Types.CHAR: case Types.VARCHAR: case Types.LONGVARCHAR: - if (field.isBinary() || !field.getConnection().isIncludeAllFields()) { + if (field.isBinary() || !field.getConnectionProperties().isIncludeAllFields()) { return true; } try { @@ -88,7 +88,7 @@ public boolean isCurrency(int column) throws SQLException { */ public int isNullable(int column) throws SQLException { FieldWithMetadata field = getField(column); - if (!field.getConnection().isIncludeAllFields()) { + if (!field.getConnectionProperties().isIncludeAllFields()) { return ResultSetMetaData.columnNullableUnknown; } return field.isNotNull() ? ResultSetMetaData.columnNoNulls : ResultSetMetaData.columnNullable; @@ -100,7 +100,7 @@ public boolean isSigned(int column) throws SQLException { public int getColumnDisplaySize(int column) throws SQLException { FieldWithMetadata field = getField(column); - if (!field.getConnection().isIncludeAllFields()) { + if (!field.getConnectionProperties().isIncludeAllFields()) { return 0; } // If we can't find a charset, we'll return 0. In that case assume 1 byte per char @@ -121,7 +121,7 @@ public String getSchemaName(int column) throws SQLException { public int getPrecision(int column) throws SQLException { FieldWithMetadata field = getField(column); - if (!field.getConnection().isIncludeAllFields()) { + if (!field.getConnectionProperties().isIncludeAllFields()) { return 0; } if (isDecimalType(field.getJavaType(), field.getVitessTypeValue())) { @@ -289,11 +289,11 @@ public boolean isDefinitelyWritable(int column) throws SQLException { public String getColumnClassName(int column) throws SQLException { FieldWithMetadata field = getField(column); - if (!field.getConnection().isIncludeAllFields()) { + if (!field.getConnectionProperties().isIncludeAllFields()) { return null; } return getClassNameForJavaType(field.getJavaType(), field.getVitessTypeValue(), field.isUnsigned(), - field.isBinary() || field.isBlob(), field.isOpaqueBinary(), field.getConnection().getYearIsDateType()); + field.isBinary() || field.isBlob(), field.isOpaqueBinary(), field.getConnectionProperties().getYearIsDateType()); } public T unwrap(Class iface) throws SQLException { diff --git a/java/jdbc/src/main/java/com/flipkart/vitess/jdbc/VitessStatement.java b/java/jdbc/src/main/java/com/flipkart/vitess/jdbc/VitessStatement.java index 6ae6302c18c..e0d463ab5ca 100644 --- a/java/jdbc/src/main/java/com/flipkart/vitess/jdbc/VitessStatement.java +++ b/java/jdbc/src/main/java/com/flipkart/vitess/jdbc/VitessStatement.java @@ -386,7 +386,7 @@ public ResultSet getGeneratedKeys() throws SQLException { } } - return new VitessResultSet(columnNames, columnTypes, data); + return new VitessResultSet(columnNames, columnTypes, data, this.vitessConnection); } /** diff --git a/java/jdbc/src/test/java/com/flipkart/vitess/jdbc/FieldWithMetadataTest.java b/java/jdbc/src/test/java/com/flipkart/vitess/jdbc/FieldWithMetadataTest.java index 4667b5445e5..4d25a0057fb 100644 --- a/java/jdbc/src/test/java/com/flipkart/vitess/jdbc/FieldWithMetadataTest.java +++ b/java/jdbc/src/test/java/com/flipkart/vitess/jdbc/FieldWithMetadataTest.java @@ -7,6 +7,7 @@ import org.junit.Assert; import org.junit.Test; import org.junit.runner.RunWith; +import org.mockito.Mockito; import org.mockito.internal.verification.VerificationModeFactory; import org.powermock.api.mockito.PowerMockito; import org.powermock.core.classloader.annotations.PrepareForTest; @@ -610,20 +611,75 @@ public void testMaxBytesPerChar() throws Exception { int second = fieldWithMetadata.getMaxBytesPerCharacter(); Assert.assertEquals("cached response is same as first", first, second); + // We called getMaxBytesPerCharacter 2 times above, but should only have made 1 call to fieldWithMetadata.getMaxBytesPerChar: + // first - call conn + // second - return cached + Mockito.verify(fieldWithMetadata, VerificationModeFactory.times(1)).getMaxBytesPerChar(33, "UTF-8"); PowerMockito.verifyPrivate(fieldWithMetadata, VerificationModeFactory.times(1)).invoke("getCollationIndex"); conn.setIncludedFields(Query.ExecuteOptions.IncludedFields.TYPE_AND_NAME); fieldWithMetadata = PowerMockito.spy(new FieldWithMetadata(conn, raw)); Assert.assertEquals("0 return value when not including all fields", 0, fieldWithMetadata.getMaxBytesPerCharacter()); - // We called getMaxBytesPerCharacter 3 times above, but should only have made 1 call to conn.getMaxBytesPerChar: - // first - call conn - // second - returne cached - // third - short circuit because not including all fields - // Will test the actual implementation/return value in VitessConnection - PowerMockito.verifyPrivate(conn, VerificationModeFactory.times(1)).invoke("getMaxBytesPerChar", 33, "UTF-8"); - + // We should not call this function because we short circuited due to not including all fields. + Mockito.verify(fieldWithMetadata, VerificationModeFactory.times(0)).getMaxBytesPerChar(33, "UTF-8"); // Should not be called at all, because it's new for just this test PowerMockito.verifyPrivate(fieldWithMetadata, VerificationModeFactory.times(0)).invoke("getCollationIndex"); } + + @Test public void testGetEncodingForIndex() throws SQLException { + Query.Field raw = Query.Field.newBuilder() + .setTable("foo") + .setType(Query.Type.CHAR) + .setName("foo") + .setOrgName("foo") + .setCharset(33) + .build(); + FieldWithMetadata field = new FieldWithMetadata(getVitessConnection(), raw); + + // No default encoding configured, and passing NO_CHARSET_INFO basically says "mysql doesn't know" + // which means don't try looking it up + Assert.assertEquals(null, field.getEncodingForIndex(MysqlDefs.NO_CHARSET_INFO)); + // Similarly, a null index or one landing out of bounds for the charset index should return null + Assert.assertEquals(null, field.getEncodingForIndex(Integer.MAX_VALUE)); + Assert.assertEquals(null, field.getEncodingForIndex(-123)); + + // charsetIndex 25 is MYSQL_CHARSET_NAME_greek, which is a charset with multiple names, ISO8859_7 and greek + // Without an encoding configured in the connection, we should return the first (default) encoding for a charset, + // in this case ISO8859_7 + Assert.assertEquals("ISO-8859-7", field.getEncodingForIndex(25)); + field.getConnectionProperties().setEncoding("greek"); + // With an encoding configured, we should return that because it matches one of the names for the charset + Assert.assertEquals("greek", field.getEncodingForIndex(25)); + + field.getConnectionProperties().setEncoding(null); + Assert.assertEquals("UTF-8", field.getEncodingForIndex(33)); + Assert.assertEquals("ISO-8859-1", field.getEncodingForIndex(63)); + + field.getConnectionProperties().setEncoding("NOT_REAL"); + // Same tests as the first one, but testing that when there is a default configured, it falls back to that regardless + Assert.assertEquals("NOT_REAL", field.getEncodingForIndex(MysqlDefs.NO_CHARSET_INFO)); + Assert.assertEquals("NOT_REAL", field.getEncodingForIndex(Integer.MAX_VALUE)); + Assert.assertEquals("NOT_REAL", field.getEncodingForIndex(-123)); + } + + @Test public void testGetMaxBytesPerChar() throws SQLException { + Query.Field raw = Query.Field.newBuilder() + .setTable("foo") + .setType(Query.Type.CHAR) + .setName("foo") + .setOrgName("foo") + .setCharset(33) + .build(); + FieldWithMetadata field = new FieldWithMetadata(getVitessConnection(), raw); + + // Default state when no good info is passed in + Assert.assertEquals(0, field.getMaxBytesPerChar(MysqlDefs.NO_CHARSET_INFO, null)); + // use passed collation index + Assert.assertEquals(3, field.getMaxBytesPerChar(CharsetMapping.MYSQL_COLLATION_INDEX_utf8, null)); + // use first, if both are passed and valid + Assert.assertEquals(3, field.getMaxBytesPerChar(CharsetMapping.MYSQL_COLLATION_INDEX_utf8, "UnicodeBig")); + // use passed default charset + Assert.assertEquals(2, field.getMaxBytesPerChar(MysqlDefs.NO_CHARSET_INFO, "UnicodeBig")); + } } diff --git a/java/jdbc/src/test/java/com/flipkart/vitess/jdbc/VitessConnectionTest.java b/java/jdbc/src/test/java/com/flipkart/vitess/jdbc/VitessConnectionTest.java index 02ac1338050..91f25bd5b1c 100644 --- a/java/jdbc/src/test/java/com/flipkart/vitess/jdbc/VitessConnectionTest.java +++ b/java/jdbc/src/test/java/com/flipkart/vitess/jdbc/VitessConnectionTest.java @@ -1,8 +1,6 @@ package com.flipkart.vitess.jdbc; import com.flipkart.vitess.util.Constants; -import com.flipkart.vitess.util.MysqlDefs; -import com.flipkart.vitess.util.charset.CharsetMapping; import com.google.common.util.concurrent.Futures; import com.youtube.vitess.client.Context; import com.youtube.vitess.client.SQLFuture; @@ -205,46 +203,4 @@ public class VitessConnectionTest extends BaseTest { Assert.assertEquals(Topodata.TabletType.REPLICA, conn.getTabletType()); Assert.assertEquals(true, conn.getBlobsAreStrings()); } - - @Test public void testGetEncodingForIndex() throws SQLException { - VitessConnection conn = getVitessConnection(); - - // No default encoding configured, and passing NO_CHARSET_INFO basically says "mysql doesn't know" - // which means don't try looking it up - Assert.assertEquals(null, conn.getEncodingForIndex(MysqlDefs.NO_CHARSET_INFO)); - // Similarly, a null index or one landing out of bounds for the charset index should return null - Assert.assertEquals(null, conn.getEncodingForIndex(Integer.MAX_VALUE)); - Assert.assertEquals(null, conn.getEncodingForIndex(-123)); - - // charsetIndex 25 is MYSQL_CHARSET_NAME_greek, which is a charset with multiple names, ISO8859_7 and greek - // Without an encoding configured in the connection, we should return the first (default) encoding for a charset, - // in this case ISO8859_7 - Assert.assertEquals("ISO-8859-7", conn.getEncodingForIndex(25)); - conn.setEncoding("greek"); - // With an encoding configured, we should return that because it matches one of the names for the charset - Assert.assertEquals("greek", conn.getEncodingForIndex(25)); - - conn.setEncoding(null); - Assert.assertEquals("UTF-8", conn.getEncodingForIndex(33)); - Assert.assertEquals("ISO-8859-1", conn.getEncodingForIndex(63)); - - conn.setEncoding("NOT_REAL"); - // Same tests as the first one, but testing that when there is a default configured, it falls back to that regardless - Assert.assertEquals("NOT_REAL", conn.getEncodingForIndex(MysqlDefs.NO_CHARSET_INFO)); - Assert.assertEquals("NOT_REAL", conn.getEncodingForIndex(Integer.MAX_VALUE)); - Assert.assertEquals("NOT_REAL", conn.getEncodingForIndex(-123)); - } - - @Test public void testGetMaxBytesPerChar() throws SQLException { - VitessConnection conn = getVitessConnection(); - - // Default state when no good info is passed in - Assert.assertEquals(0, conn.getMaxBytesPerChar(MysqlDefs.NO_CHARSET_INFO, null)); - // use passed collation index - Assert.assertEquals(3, conn.getMaxBytesPerChar(CharsetMapping.MYSQL_COLLATION_INDEX_utf8, null)); - // use first, if both are passed and valid - Assert.assertEquals(3, conn.getMaxBytesPerChar(CharsetMapping.MYSQL_COLLATION_INDEX_utf8, "UnicodeBig")); - // use passed default charset - Assert.assertEquals(2, conn.getMaxBytesPerChar(MysqlDefs.NO_CHARSET_INFO, "UnicodeBig")); - } } diff --git a/java/jdbc/src/test/java/com/flipkart/vitess/jdbc/VitessDatabaseMetadataTest.java b/java/jdbc/src/test/java/com/flipkart/vitess/jdbc/VitessDatabaseMetadataTest.java index 6f3bf170d85..c228305ca0f 100644 --- a/java/jdbc/src/test/java/com/flipkart/vitess/jdbc/VitessDatabaseMetadataTest.java +++ b/java/jdbc/src/test/java/com/flipkart/vitess/jdbc/VitessDatabaseMetadataTest.java @@ -1,11 +1,11 @@ package com.flipkart.vitess.jdbc; -import com.flipkart.vitess.jdbc.*; import com.flipkart.vitess.util.Constants; import com.google.protobuf.ByteString; import com.youtube.vitess.client.cursor.Cursor; import com.youtube.vitess.client.cursor.SimpleCursor; import com.youtube.vitess.proto.Query; + import org.junit.Assert; import org.junit.Test; import org.junit.runner.RunWith; @@ -13,14 +13,18 @@ import org.powermock.core.classloader.annotations.PrepareForTest; import org.powermock.modules.junit4.PowerMockRunner; -import java.sql.*; +import java.sql.Connection; +import java.sql.ResultSet; +import java.sql.ResultSetMetaData; +import java.sql.SQLException; +import java.sql.Types; import java.util.ArrayList; import java.util.Properties; /** * Created by ashudeep.sharma on 08/03/16. */ -@RunWith(PowerMockRunner.class) @PrepareForTest(VitessMySQLDatabaseMetadata.class) public class VitessDatabaseMetadataTest { +@RunWith(PowerMockRunner.class) @PrepareForTest(VitessMySQLDatabaseMetadata.class) public class VitessDatabaseMetadataTest extends BaseTest { private ResultSet resultSet; @@ -936,7 +940,38 @@ @Test public void getTablesTest() throws SQLException, Exception { String sql = "SHOW FULL TABLES FROM `vt` LIKE '%'"; - Cursor mockedCursor = new SimpleCursor(Query.QueryResult.newBuilder() + Cursor mockedCursor = getTablesCursor(); + + VitessStatement vitessStatement = PowerMockito.mock(VitessStatement.class); + PowerMockito.whenNew(VitessStatement.class).withAnyArguments().thenReturn(vitessStatement); + PowerMockito.when(vitessStatement.executeQuery(sql)) + .thenReturn(new VitessResultSet(mockedCursor)); + + VitessDatabaseMetaData vitessDatabaseMetaData = new VitessMySQLDatabaseMetadata(getVitessConnection()); + ResultSet actualResultSet = vitessDatabaseMetaData.getTables("vt", null, null, null); + ResultSet expectedResultSet = new VitessResultSet(mockedCursor); + + assertResultSetEquals(actualResultSet, expectedResultSet); + } + + @Test public void getTablesProperResultTypeTest() throws SQLException, Exception { + + String sql = "SHOW FULL TABLES FROM `vt` LIKE '%'"; + Cursor mockedCursor = getTablesCursor(); + + VitessStatement vitessStatement = PowerMockito.mock(VitessStatement.class); + PowerMockito.whenNew(VitessStatement.class).withAnyArguments().thenReturn(vitessStatement); + PowerMockito.when(vitessStatement.executeQuery(sql)) + .thenReturn(new VitessResultSet(mockedCursor)); + + VitessDatabaseMetaData vitessDatabaseMetaData = new VitessMySQLDatabaseMetadata(getVitessConnection()); + ResultSet actualResultSet = vitessDatabaseMetaData.getTables("vt", null, null, null); + actualResultSet.next(); + Assert.assertEquals(String.class, actualResultSet.getObject("TABLE_CAT").getClass()); + } + + private Cursor getTablesCursor() throws Exception { + return new SimpleCursor(Query.QueryResult.newBuilder() .addFields(Query.Field.newBuilder().setName("TABLE_CAT").setType(Query.Type.VARCHAR)) .addFields(Query.Field.newBuilder().setName("TABLE_SCHEM").setType(Query.Type.VARCHAR)) .addFields(Query.Field.newBuilder().setName("TABLE_NAME").setType(Query.Type.VARCHAR)) @@ -974,17 +1009,6 @@ .addLengths("".length()).addLengths("".length()).addLengths("".length()) .setValues(ByteString.copyFromUtf8("TestDB2SampleLocalTemporaryLOCAL TEMPORARY"))) .build()); - - VitessStatement vitessStatement = PowerMockito.mock(VitessStatement.class); - PowerMockito.whenNew(VitessStatement.class).withAnyArguments().thenReturn(vitessStatement); - PowerMockito.when(vitessStatement.executeQuery(sql)) - .thenReturn(new VitessResultSet(mockedCursor)); - - VitessDatabaseMetaData vitessDatabaseMetaData = new VitessMySQLDatabaseMetadata(null); - ResultSet actualResultSet = vitessDatabaseMetaData.getTables("vt", null, null, null); - ResultSet expectedResultSet = new VitessResultSet(mockedCursor); - - assertResultSetEquals(actualResultSet, expectedResultSet); } @Test public void getColumnsTest() throws SQLException, Exception { diff --git a/php/src/Vitess/Proto/Vtgate/ExecuteBatchRequest.php b/php/src/Vitess/Proto/Vtgate/ExecuteBatchRequest.php index 4c9e0745369..cdf45ee8eab 100644 --- a/php/src/Vitess/Proto/Vtgate/ExecuteBatchRequest.php +++ b/php/src/Vitess/Proto/Vtgate/ExecuteBatchRequest.php @@ -22,7 +22,7 @@ class ExecuteBatchRequest extends \DrSlump\Protobuf\Message { public $as_transaction = null; /** @var string */ - public $keyspace = null; + public $keyspace_shard = null; /** @var \Vitess\Proto\Query\ExecuteOptions */ public $options = null; @@ -79,10 +79,10 @@ public static function descriptor() $f->rule = \DrSlump\Protobuf::RULE_OPTIONAL; $descriptor->addField($f); - // OPTIONAL STRING keyspace = 6 + // OPTIONAL STRING keyspace_shard = 6 $f = new \DrSlump\Protobuf\Field(); $f->number = 6; - $f->name = "keyspace"; + $f->name = "keyspace_shard"; $f->type = \DrSlump\Protobuf::TYPE_STRING; $f->rule = \DrSlump\Protobuf::RULE_OPTIONAL; $descriptor->addField($f); @@ -309,39 +309,39 @@ public function setAsTransaction( $value){ } /** - * Check if has a value + * Check if has a value * * @return boolean */ - public function hasKeyspace(){ + public function hasKeyspaceShard(){ return $this->_has(6); } /** - * Clear value + * Clear value * * @return \Vitess\Proto\Vtgate\ExecuteBatchRequest */ - public function clearKeyspace(){ + public function clearKeyspaceShard(){ return $this->_clear(6); } /** - * Get value + * Get value * * @return string */ - public function getKeyspace(){ + public function getKeyspaceShard(){ return $this->_get(6); } /** - * Set value + * Set value * * @param string $value * @return \Vitess\Proto\Vtgate\ExecuteBatchRequest */ - public function setKeyspace( $value){ + public function setKeyspaceShard( $value){ return $this->_set(6, $value); } diff --git a/php/src/Vitess/Proto/Vtgate/ExecuteRequest.php b/php/src/Vitess/Proto/Vtgate/ExecuteRequest.php index 4f988246a76..85619f3acd6 100644 --- a/php/src/Vitess/Proto/Vtgate/ExecuteRequest.php +++ b/php/src/Vitess/Proto/Vtgate/ExecuteRequest.php @@ -22,7 +22,7 @@ class ExecuteRequest extends \DrSlump\Protobuf\Message { public $not_in_transaction = null; /** @var string */ - public $keyspace = null; + public $keyspace_shard = null; /** @var \Vitess\Proto\Query\ExecuteOptions */ public $options = null; @@ -79,10 +79,10 @@ public static function descriptor() $f->rule = \DrSlump\Protobuf::RULE_OPTIONAL; $descriptor->addField($f); - // OPTIONAL STRING keyspace = 6 + // OPTIONAL STRING keyspace_shard = 6 $f = new \DrSlump\Protobuf\Field(); $f->number = 6; - $f->name = "keyspace"; + $f->name = "keyspace_shard"; $f->type = \DrSlump\Protobuf::TYPE_STRING; $f->rule = \DrSlump\Protobuf::RULE_OPTIONAL; $descriptor->addField($f); @@ -289,39 +289,39 @@ public function setNotInTransaction( $value){ } /** - * Check if has a value + * Check if has a value * * @return boolean */ - public function hasKeyspace(){ + public function hasKeyspaceShard(){ return $this->_has(6); } /** - * Clear value + * Clear value * * @return \Vitess\Proto\Vtgate\ExecuteRequest */ - public function clearKeyspace(){ + public function clearKeyspaceShard(){ return $this->_clear(6); } /** - * Get value + * Get value * * @return string */ - public function getKeyspace(){ + public function getKeyspaceShard(){ return $this->_get(6); } /** - * Set value + * Set value * * @param string $value * @return \Vitess\Proto\Vtgate\ExecuteRequest */ - public function setKeyspace( $value){ + public function setKeyspaceShard( $value){ return $this->_set(6, $value); } diff --git a/php/src/Vitess/Proto/Vtgate/Session.php b/php/src/Vitess/Proto/Vtgate/Session.php index 9ab873b93a9..07acfc7e6dc 100644 --- a/php/src/Vitess/Proto/Vtgate/Session.php +++ b/php/src/Vitess/Proto/Vtgate/Session.php @@ -15,6 +15,9 @@ class Session extends \DrSlump\Protobuf\Message { /** @var boolean */ public $single_db = null; + /** @var boolean */ + public $autocommit = null; + /** @var \Closure[] */ protected static $__extensions = array(); @@ -48,6 +51,14 @@ public static function descriptor() $f->rule = \DrSlump\Protobuf::RULE_OPTIONAL; $descriptor->addField($f); + // OPTIONAL BOOL autocommit = 4 + $f = new \DrSlump\Protobuf\Field(); + $f->number = 4; + $f->name = "autocommit"; + $f->type = \DrSlump\Protobuf::TYPE_BOOL; + $f->rule = \DrSlump\Protobuf::RULE_OPTIONAL; + $descriptor->addField($f); + foreach (self::$__extensions as $cb) { $descriptor->addField($cb(), true); } @@ -185,6 +196,43 @@ public function getSingleDb(){ public function setSingleDb( $value){ return $this->_set(3, $value); } + + /** + * Check if has a value + * + * @return boolean + */ + public function hasAutocommit(){ + return $this->_has(4); + } + + /** + * Clear value + * + * @return \Vitess\Proto\Vtgate\Session + */ + public function clearAutocommit(){ + return $this->_clear(4); + } + + /** + * Get value + * + * @return boolean + */ + public function getAutocommit(){ + return $this->_get(4); + } + + /** + * Set value + * + * @param boolean $value + * @return \Vitess\Proto\Vtgate\Session + */ + public function setAutocommit( $value){ + return $this->_set(4, $value); + } } } diff --git a/php/src/Vitess/Proto/Vtgate/StreamExecuteRequest.php b/php/src/Vitess/Proto/Vtgate/StreamExecuteRequest.php index 497be63103d..ab34c5e648d 100644 --- a/php/src/Vitess/Proto/Vtgate/StreamExecuteRequest.php +++ b/php/src/Vitess/Proto/Vtgate/StreamExecuteRequest.php @@ -16,7 +16,7 @@ class StreamExecuteRequest extends \DrSlump\Protobuf\Message { public $tablet_type = null; /** @var string */ - public $keyspace = null; + public $keyspace_shard = null; /** @var \Vitess\Proto\Query\ExecuteOptions */ public $options = null; @@ -56,10 +56,10 @@ public static function descriptor() $f->reference = '\Vitess\Proto\Topodata\TabletType'; $descriptor->addField($f); - // OPTIONAL STRING keyspace = 4 + // OPTIONAL STRING keyspace_shard = 4 $f = new \DrSlump\Protobuf\Field(); $f->number = 4; - $f->name = "keyspace"; + $f->name = "keyspace_shard"; $f->type = \DrSlump\Protobuf::TYPE_STRING; $f->rule = \DrSlump\Protobuf::RULE_OPTIONAL; $descriptor->addField($f); @@ -192,39 +192,39 @@ public function setTabletType( $value){ } /** - * Check if has a value + * Check if has a value * * @return boolean */ - public function hasKeyspace(){ + public function hasKeyspaceShard(){ return $this->_has(4); } /** - * Clear value + * Clear value * * @return \Vitess\Proto\Vtgate\StreamExecuteRequest */ - public function clearKeyspace(){ + public function clearKeyspaceShard(){ return $this->_clear(4); } /** - * Get value + * Get value * * @return string */ - public function getKeyspace(){ + public function getKeyspaceShard(){ return $this->_get(4); } /** - * Set value + * Set value * * @param string $value * @return \Vitess\Proto\Vtgate\StreamExecuteRequest */ - public function setKeyspace( $value){ + public function setKeyspaceShard( $value){ return $this->_set(4, $value); } diff --git a/php/src/Vitess/Proto/Workflow/Task.php b/php/src/Vitess/Proto/Workflow/Task.php new file mode 100644 index 00000000000..4c37cb566b7 --- /dev/null +++ b/php/src/Vitess/Proto/Workflow/Task.php @@ -0,0 +1,239 @@ +number = 1; + $f->name = "id"; + $f->type = \DrSlump\Protobuf::TYPE_STRING; + $f->rule = \DrSlump\Protobuf::RULE_OPTIONAL; + $descriptor->addField($f); + + // OPTIONAL ENUM state = 2 + $f = new \DrSlump\Protobuf\Field(); + $f->number = 2; + $f->name = "state"; + $f->type = \DrSlump\Protobuf::TYPE_ENUM; + $f->rule = \DrSlump\Protobuf::RULE_OPTIONAL; + $f->reference = '\Vitess\Proto\Workflow\TaskState'; + $descriptor->addField($f); + + // REPEATED MESSAGE attributes = 3 + $f = new \DrSlump\Protobuf\Field(); + $f->number = 3; + $f->name = "attributes"; + $f->type = \DrSlump\Protobuf::TYPE_MESSAGE; + $f->rule = \DrSlump\Protobuf::RULE_REPEATED; + $f->reference = '\Vitess\Proto\Workflow\Task\AttributesEntry'; + $descriptor->addField($f); + + // OPTIONAL STRING error = 4 + $f = new \DrSlump\Protobuf\Field(); + $f->number = 4; + $f->name = "error"; + $f->type = \DrSlump\Protobuf::TYPE_STRING; + $f->rule = \DrSlump\Protobuf::RULE_OPTIONAL; + $descriptor->addField($f); + + foreach (self::$__extensions as $cb) { + $descriptor->addField($cb(), true); + } + + return $descriptor; + } + + /** + * Check if has a value + * + * @return boolean + */ + public function hasId(){ + return $this->_has(1); + } + + /** + * Clear value + * + * @return \Vitess\Proto\Workflow\Task + */ + public function clearId(){ + return $this->_clear(1); + } + + /** + * Get value + * + * @return string + */ + public function getId(){ + return $this->_get(1); + } + + /** + * Set value + * + * @param string $value + * @return \Vitess\Proto\Workflow\Task + */ + public function setId( $value){ + return $this->_set(1, $value); + } + + /** + * Check if has a value + * + * @return boolean + */ + public function hasState(){ + return $this->_has(2); + } + + /** + * Clear value + * + * @return \Vitess\Proto\Workflow\Task + */ + public function clearState(){ + return $this->_clear(2); + } + + /** + * Get value + * + * @return int - \Vitess\Proto\Workflow\TaskState + */ + public function getState(){ + return $this->_get(2); + } + + /** + * Set value + * + * @param int - \Vitess\Proto\Workflow\TaskState $value + * @return \Vitess\Proto\Workflow\Task + */ + public function setState( $value){ + return $this->_set(2, $value); + } + + /** + * Check if has a value + * + * @return boolean + */ + public function hasAttributes(){ + return $this->_has(3); + } + + /** + * Clear value + * + * @return \Vitess\Proto\Workflow\Task + */ + public function clearAttributes(){ + return $this->_clear(3); + } + + /** + * Get value + * + * @param int $idx + * @return \Vitess\Proto\Workflow\Task\AttributesEntry + */ + public function getAttributes($idx = NULL){ + return $this->_get(3, $idx); + } + + /** + * Set value + * + * @param \Vitess\Proto\Workflow\Task\AttributesEntry $value + * @return \Vitess\Proto\Workflow\Task + */ + public function setAttributes(\Vitess\Proto\Workflow\Task\AttributesEntry $value, $idx = NULL){ + return $this->_set(3, $value, $idx); + } + + /** + * Get all elements of + * + * @return \Vitess\Proto\Workflow\Task\AttributesEntry[] + */ + public function getAttributesList(){ + return $this->_get(3); + } + + /** + * Add a new element to + * + * @param \Vitess\Proto\Workflow\Task\AttributesEntry $value + * @return \Vitess\Proto\Workflow\Task + */ + public function addAttributes(\Vitess\Proto\Workflow\Task\AttributesEntry $value){ + return $this->_add(3, $value); + } + + /** + * Check if has a value + * + * @return boolean + */ + public function hasError(){ + return $this->_has(4); + } + + /** + * Clear value + * + * @return \Vitess\Proto\Workflow\Task + */ + public function clearError(){ + return $this->_clear(4); + } + + /** + * Get value + * + * @return string + */ + public function getError(){ + return $this->_get(4); + } + + /** + * Set value + * + * @param string $value + * @return \Vitess\Proto\Workflow\Task + */ + public function setError( $value){ + return $this->_set(4, $value); + } + } +} + diff --git a/php/src/Vitess/Proto/Workflow/Task/AttributesEntry.php b/php/src/Vitess/Proto/Workflow/Task/AttributesEntry.php new file mode 100644 index 00000000000..80b0c37585f --- /dev/null +++ b/php/src/Vitess/Proto/Workflow/Task/AttributesEntry.php @@ -0,0 +1,121 @@ +number = 1; + $f->name = "key"; + $f->type = \DrSlump\Protobuf::TYPE_STRING; + $f->rule = \DrSlump\Protobuf::RULE_OPTIONAL; + $descriptor->addField($f); + + // OPTIONAL STRING value = 2 + $f = new \DrSlump\Protobuf\Field(); + $f->number = 2; + $f->name = "value"; + $f->type = \DrSlump\Protobuf::TYPE_STRING; + $f->rule = \DrSlump\Protobuf::RULE_OPTIONAL; + $descriptor->addField($f); + + foreach (self::$__extensions as $cb) { + $descriptor->addField($cb(), true); + } + + return $descriptor; + } + + /** + * Check if has a value + * + * @return boolean + */ + public function hasKey(){ + return $this->_has(1); + } + + /** + * Clear value + * + * @return \Vitess\Proto\Workflow\Task\AttributesEntry + */ + public function clearKey(){ + return $this->_clear(1); + } + + /** + * Get value + * + * @return string + */ + public function getKey(){ + return $this->_get(1); + } + + /** + * Set value + * + * @param string $value + * @return \Vitess\Proto\Workflow\Task\AttributesEntry + */ + public function setKey( $value){ + return $this->_set(1, $value); + } + + /** + * Check if has a value + * + * @return boolean + */ + public function hasValue(){ + return $this->_has(2); + } + + /** + * Clear value + * + * @return \Vitess\Proto\Workflow\Task\AttributesEntry + */ + public function clearValue(){ + return $this->_clear(2); + } + + /** + * Get value + * + * @return string + */ + public function getValue(){ + return $this->_get(2); + } + + /** + * Set value + * + * @param string $value + * @return \Vitess\Proto\Workflow\Task\AttributesEntry + */ + public function setValue( $value){ + return $this->_set(2, $value); + } + } +} + diff --git a/php/src/Vitess/Proto/Workflow/TaskState.php b/php/src/Vitess/Proto/Workflow/TaskState.php new file mode 100644 index 00000000000..5313cb00f43 --- /dev/null +++ b/php/src/Vitess/Proto/Workflow/TaskState.php @@ -0,0 +1,12 @@ +number = 1; + $f->name = "code_version"; + $f->type = \DrSlump\Protobuf::TYPE_INT32; + $f->rule = \DrSlump\Protobuf::RULE_OPTIONAL; + $descriptor->addField($f); + + // REPEATED MESSAGE tasks = 2 + $f = new \DrSlump\Protobuf\Field(); + $f->number = 2; + $f->name = "tasks"; + $f->type = \DrSlump\Protobuf::TYPE_MESSAGE; + $f->rule = \DrSlump\Protobuf::RULE_REPEATED; + $f->reference = '\Vitess\Proto\Workflow\WorkflowCheckpoint\TasksEntry'; + $descriptor->addField($f); + + // REPEATED MESSAGE settings = 3 + $f = new \DrSlump\Protobuf\Field(); + $f->number = 3; + $f->name = "settings"; + $f->type = \DrSlump\Protobuf::TYPE_MESSAGE; + $f->rule = \DrSlump\Protobuf::RULE_REPEATED; + $f->reference = '\Vitess\Proto\Workflow\WorkflowCheckpoint\SettingsEntry'; + $descriptor->addField($f); + + foreach (self::$__extensions as $cb) { + $descriptor->addField($cb(), true); + } + + return $descriptor; + } + + /** + * Check if has a value + * + * @return boolean + */ + public function hasCodeVersion(){ + return $this->_has(1); + } + + /** + * Clear value + * + * @return \Vitess\Proto\Workflow\WorkflowCheckpoint + */ + public function clearCodeVersion(){ + return $this->_clear(1); + } + + /** + * Get value + * + * @return int + */ + public function getCodeVersion(){ + return $this->_get(1); + } + + /** + * Set value + * + * @param int $value + * @return \Vitess\Proto\Workflow\WorkflowCheckpoint + */ + public function setCodeVersion( $value){ + return $this->_set(1, $value); + } + + /** + * Check if has a value + * + * @return boolean + */ + public function hasTasks(){ + return $this->_has(2); + } + + /** + * Clear value + * + * @return \Vitess\Proto\Workflow\WorkflowCheckpoint + */ + public function clearTasks(){ + return $this->_clear(2); + } + + /** + * Get value + * + * @param int $idx + * @return \Vitess\Proto\Workflow\WorkflowCheckpoint\TasksEntry + */ + public function getTasks($idx = NULL){ + return $this->_get(2, $idx); + } + + /** + * Set value + * + * @param \Vitess\Proto\Workflow\WorkflowCheckpoint\TasksEntry $value + * @return \Vitess\Proto\Workflow\WorkflowCheckpoint + */ + public function setTasks(\Vitess\Proto\Workflow\WorkflowCheckpoint\TasksEntry $value, $idx = NULL){ + return $this->_set(2, $value, $idx); + } + + /** + * Get all elements of + * + * @return \Vitess\Proto\Workflow\WorkflowCheckpoint\TasksEntry[] + */ + public function getTasksList(){ + return $this->_get(2); + } + + /** + * Add a new element to + * + * @param \Vitess\Proto\Workflow\WorkflowCheckpoint\TasksEntry $value + * @return \Vitess\Proto\Workflow\WorkflowCheckpoint + */ + public function addTasks(\Vitess\Proto\Workflow\WorkflowCheckpoint\TasksEntry $value){ + return $this->_add(2, $value); + } + + /** + * Check if has a value + * + * @return boolean + */ + public function hasSettings(){ + return $this->_has(3); + } + + /** + * Clear value + * + * @return \Vitess\Proto\Workflow\WorkflowCheckpoint + */ + public function clearSettings(){ + return $this->_clear(3); + } + + /** + * Get value + * + * @param int $idx + * @return \Vitess\Proto\Workflow\WorkflowCheckpoint\SettingsEntry + */ + public function getSettings($idx = NULL){ + return $this->_get(3, $idx); + } + + /** + * Set value + * + * @param \Vitess\Proto\Workflow\WorkflowCheckpoint\SettingsEntry $value + * @return \Vitess\Proto\Workflow\WorkflowCheckpoint + */ + public function setSettings(\Vitess\Proto\Workflow\WorkflowCheckpoint\SettingsEntry $value, $idx = NULL){ + return $this->_set(3, $value, $idx); + } + + /** + * Get all elements of + * + * @return \Vitess\Proto\Workflow\WorkflowCheckpoint\SettingsEntry[] + */ + public function getSettingsList(){ + return $this->_get(3); + } + + /** + * Add a new element to + * + * @param \Vitess\Proto\Workflow\WorkflowCheckpoint\SettingsEntry $value + * @return \Vitess\Proto\Workflow\WorkflowCheckpoint + */ + public function addSettings(\Vitess\Proto\Workflow\WorkflowCheckpoint\SettingsEntry $value){ + return $this->_add(3, $value); + } + } +} + diff --git a/php/src/Vitess/Proto/Workflow/WorkflowCheckpoint/SettingsEntry.php b/php/src/Vitess/Proto/Workflow/WorkflowCheckpoint/SettingsEntry.php new file mode 100644 index 00000000000..896b166098b --- /dev/null +++ b/php/src/Vitess/Proto/Workflow/WorkflowCheckpoint/SettingsEntry.php @@ -0,0 +1,121 @@ +number = 1; + $f->name = "key"; + $f->type = \DrSlump\Protobuf::TYPE_STRING; + $f->rule = \DrSlump\Protobuf::RULE_OPTIONAL; + $descriptor->addField($f); + + // OPTIONAL STRING value = 2 + $f = new \DrSlump\Protobuf\Field(); + $f->number = 2; + $f->name = "value"; + $f->type = \DrSlump\Protobuf::TYPE_STRING; + $f->rule = \DrSlump\Protobuf::RULE_OPTIONAL; + $descriptor->addField($f); + + foreach (self::$__extensions as $cb) { + $descriptor->addField($cb(), true); + } + + return $descriptor; + } + + /** + * Check if has a value + * + * @return boolean + */ + public function hasKey(){ + return $this->_has(1); + } + + /** + * Clear value + * + * @return \Vitess\Proto\Workflow\WorkflowCheckpoint\SettingsEntry + */ + public function clearKey(){ + return $this->_clear(1); + } + + /** + * Get value + * + * @return string + */ + public function getKey(){ + return $this->_get(1); + } + + /** + * Set value + * + * @param string $value + * @return \Vitess\Proto\Workflow\WorkflowCheckpoint\SettingsEntry + */ + public function setKey( $value){ + return $this->_set(1, $value); + } + + /** + * Check if has a value + * + * @return boolean + */ + public function hasValue(){ + return $this->_has(2); + } + + /** + * Clear value + * + * @return \Vitess\Proto\Workflow\WorkflowCheckpoint\SettingsEntry + */ + public function clearValue(){ + return $this->_clear(2); + } + + /** + * Get value + * + * @return string + */ + public function getValue(){ + return $this->_get(2); + } + + /** + * Set value + * + * @param string $value + * @return \Vitess\Proto\Workflow\WorkflowCheckpoint\SettingsEntry + */ + public function setValue( $value){ + return $this->_set(2, $value); + } + } +} + diff --git a/php/src/Vitess/Proto/Workflow/WorkflowCheckpoint/TasksEntry.php b/php/src/Vitess/Proto/Workflow/WorkflowCheckpoint/TasksEntry.php new file mode 100644 index 00000000000..065825bf2ef --- /dev/null +++ b/php/src/Vitess/Proto/Workflow/WorkflowCheckpoint/TasksEntry.php @@ -0,0 +1,122 @@ +number = 1; + $f->name = "key"; + $f->type = \DrSlump\Protobuf::TYPE_STRING; + $f->rule = \DrSlump\Protobuf::RULE_OPTIONAL; + $descriptor->addField($f); + + // OPTIONAL MESSAGE value = 2 + $f = new \DrSlump\Protobuf\Field(); + $f->number = 2; + $f->name = "value"; + $f->type = \DrSlump\Protobuf::TYPE_MESSAGE; + $f->rule = \DrSlump\Protobuf::RULE_OPTIONAL; + $f->reference = '\Vitess\Proto\Workflow\Task'; + $descriptor->addField($f); + + foreach (self::$__extensions as $cb) { + $descriptor->addField($cb(), true); + } + + return $descriptor; + } + + /** + * Check if has a value + * + * @return boolean + */ + public function hasKey(){ + return $this->_has(1); + } + + /** + * Clear value + * + * @return \Vitess\Proto\Workflow\WorkflowCheckpoint\TasksEntry + */ + public function clearKey(){ + return $this->_clear(1); + } + + /** + * Get value + * + * @return string + */ + public function getKey(){ + return $this->_get(1); + } + + /** + * Set value + * + * @param string $value + * @return \Vitess\Proto\Workflow\WorkflowCheckpoint\TasksEntry + */ + public function setKey( $value){ + return $this->_set(1, $value); + } + + /** + * Check if has a value + * + * @return boolean + */ + public function hasValue(){ + return $this->_has(2); + } + + /** + * Clear value + * + * @return \Vitess\Proto\Workflow\WorkflowCheckpoint\TasksEntry + */ + public function clearValue(){ + return $this->_clear(2); + } + + /** + * Get value + * + * @return \Vitess\Proto\Workflow\Task + */ + public function getValue(){ + return $this->_get(2); + } + + /** + * Set value + * + * @param \Vitess\Proto\Workflow\Task $value + * @return \Vitess\Proto\Workflow\WorkflowCheckpoint\TasksEntry + */ + public function setValue(\Vitess\Proto\Workflow\Task $value){ + return $this->_set(2, $value); + } + } +} + diff --git a/php/src/Vitess/VTGateConn.php b/php/src/Vitess/VTGateConn.php index 10a57fd718d..9c8523d5cf3 100644 --- a/php/src/Vitess/VTGateConn.php +++ b/php/src/Vitess/VTGateConn.php @@ -45,7 +45,7 @@ public function execute(Context $ctx, $query, array $bind_vars, $tablet_type) $request = new Proto\Vtgate\ExecuteRequest(); $request->setQuery(ProtoUtils::BoundQuery($query, $bind_vars)); $request->setTabletType($tablet_type); - $request->setKeyspace($this->keyspace); + $request->setKeyspaceShard($this->keyspace); if ($ctx->getCallerId()) { $request->setCallerId($ctx->getCallerId()); } @@ -170,7 +170,7 @@ public function streamExecute(Context $ctx, $query, array $bind_vars, $tablet_ty $request = new Proto\Vtgate\StreamExecuteRequest(); $request->setQuery(ProtoUtils::BoundQuery($query, $bind_vars)); $request->setTabletType($tablet_type); - $request->setKeyspace($this->keyspace); + $request->setKeyspaceShard($this->keyspace); if ($ctx->getCallerId()) { $request->setCallerId($ctx->getCallerId()); } diff --git a/php/src/Vitess/VTGateTx.php b/php/src/Vitess/VTGateTx.php index 842fabb6577..c86f2282a17 100644 --- a/php/src/Vitess/VTGateTx.php +++ b/php/src/Vitess/VTGateTx.php @@ -32,7 +32,7 @@ public function execute(Context $ctx, $query, array $bind_vars, $tablet_type = P $request->setSession($this->session); $request->setQuery(ProtoUtils::BoundQuery($query, $bind_vars)); $request->setTabletType($tablet_type); - $request->setKeyspace($this->keyspace); + $request->setKeyspaceShard($this->keyspace); if ($ctx->getCallerId()) { $request->setCallerId($ctx->getCallerId()); } diff --git a/proto/README.md b/proto/README.md new file mode 100644 index 00000000000..50c62dbefb9 --- /dev/null +++ b/proto/README.md @@ -0,0 +1,54 @@ +# Vitess Protobuf Definitions + +This directory contains all Vitess protobuf definitions. + +Our protobuf messages are both used as wire format (e.g. `query.proto`) and for +storage (e.g. `topodata.proto`). + +RPC messages and service definitions are in separate files (e.g. `vtgate.proto` +and `vtgateservice.proto`) on purpose because our internal deployment does not +use gRPC. + +## Style Guide + +Before creating new messages or services, please make yourself familiar with the +style of the existing definitions first. + +Additionally, new definitions must adhere to the Google Cloud API Design Guide: +https://cloud.google.com/apis/design/ + +### Comments + +We are more strict than the Design Guide on the format for comments. Similar to +comments for Go types or fields, protobuf comments must start with the name. +For example: +```protobuf +// TabletAlias is a globally unique tablet identifier. +message TabletAlias { + // cell is the cell (or datacenter) the tablet is in. + string cell = 1; + ... +} +``` + +Note that the [Design Guide also has the following ask](https://cloud.google.com/apis/design/documentation#field_and_parameter_descriptions): + +> If the field value is required, input only, output only, it must be documented +> at the start of the field description. By default, all fields and parameters +> are optional. + +Here's an example which combines this ask with our stricter comments style: + +```protobuf +// ExecuteKeyspaceIdsRequest is the payload to ExecuteKeyspaceIds. +message ExecuteKeyspaceIdsRequest { + ... + // Required. keyspace to target the query to. + string keyspace = 4; + ... +} +``` + +Note that most of our existing files (as of March 2017) do not have e.g. +`"Required."` comments. Nonetheless, new files should follow this where +applicable. diff --git a/proto/vtgate.proto b/proto/vtgate.proto index 1494d73483c..b1918b52e4d 100644 --- a/proto/vtgate.proto +++ b/proto/vtgate.proto @@ -25,6 +25,9 @@ message Session { // single_db specifies if the transaction should be restricted // to a single database. bool single_db = 3; + + // autocommit specifies if the session is in autocommit mode. + bool autocommit = 4; } // ExecuteRequest is the payload to Execute. @@ -46,8 +49,8 @@ message ExecuteRequest { // not_in_transaction is deprecated and should not be used. bool not_in_transaction = 5; - // keyspace to target the query to. - string keyspace = 6; + // keyspace_shard can be 'keyspace' or 'keyspace/shard'. + string keyspace_shard = 6; // options query.ExecuteOptions options = 7; @@ -272,14 +275,13 @@ message ExecuteBatchRequest { // tablet_type is the type of tablets that these queries is targeted to. topodata.TabletType tablet_type = 4; - // as_transaction will execute the queries in this batch in a single transaction per shard, - // created for this purpose. - // (this can be seen as adding a 'begin' before and 'commit' after the queries). - // Only makes sense if tablet_type is master. If set, the Session is ignored. + // as_transaction is deprecated. + // We cannot use the proto3 deprecated feature yet because + // the php compiler doesn't recognize that construct. bool as_transaction = 5; - // keyspace to target the queries to. - string keyspace = 6; + // keyspace_shard can be 'keyspace' or 'keyspace/shard'. + string keyspace_shard = 6; // options query.ExecuteOptions options = 7; @@ -418,8 +420,8 @@ message StreamExecuteRequest { // tablet_type is the type of tablets that this query is targeted to. topodata.TabletType tablet_type = 3; - // keyspace to target the query to. - string keyspace = 4; + // keyspace_shard can be 'keyspace' or 'keyspace/shard'. + string keyspace_shard = 4; // options query.ExecuteOptions options = 5; diff --git a/py/vtdb/proto3_encoding.py b/py/vtdb/proto3_encoding.py index e734cc129e3..48ec9cfe416 100644 --- a/py/vtdb/proto3_encoding.py +++ b/py/vtdb/proto3_encoding.py @@ -436,7 +436,7 @@ def execute_request_and_name(self, sql, bind_variables, tablet_type, else: request = vtgate_pb2.ExecuteRequest() if keyspace_name: - request.keyspace = keyspace_name + request.keyspace_shard = keyspace_name routing_kwargs = {} method_name = 'Execute' @@ -676,7 +676,7 @@ def stream_execute_request_and_name(self, sql, bind_variables, tablet_type, else: request = vtgate_pb2.StreamExecuteRequest() if keyspace_name: - request.keyspace = keyspace_name + request.keyspace_shard = keyspace_name routing_kwargs = {} method_name = 'StreamExecute' diff --git a/py/vtproto/vtgate_pb2.py b/py/vtproto/vtgate_pb2.py index 95642936f11..36c36e8b8b3 100644 --- a/py/vtproto/vtgate_pb2.py +++ b/py/vtproto/vtgate_pb2.py @@ -22,7 +22,7 @@ name='vtgate.proto', package='vtgate', syntax='proto3', - serialized_pb=_b('\n\x0cvtgate.proto\x12\x06vtgate\x1a\x0bquery.proto\x1a\x0etopodata.proto\x1a\x0bvtrpc.proto\"\xb1\x01\n\x07Session\x12\x16\n\x0ein_transaction\x18\x01 \x01(\x08\x12\x34\n\x0eshard_sessions\x18\x02 \x03(\x0b\x32\x1c.vtgate.Session.ShardSession\x12\x11\n\tsingle_db\x18\x03 \x01(\x08\x1a\x45\n\x0cShardSession\x12\x1d\n\x06target\x18\x01 \x01(\x0b\x32\r.query.Target\x12\x16\n\x0etransaction_id\x18\x02 \x01(\x03\"\xf9\x01\n\x0e\x45xecuteRequest\x12\"\n\tcaller_id\x18\x01 \x01(\x0b\x32\x0f.vtrpc.CallerID\x12 \n\x07session\x18\x02 \x01(\x0b\x32\x0f.vtgate.Session\x12 \n\x05query\x18\x03 \x01(\x0b\x32\x11.query.BoundQuery\x12)\n\x0btablet_type\x18\x04 \x01(\x0e\x32\x14.topodata.TabletType\x12\x1a\n\x12not_in_transaction\x18\x05 \x01(\x08\x12\x10\n\x08keyspace\x18\x06 \x01(\t\x12&\n\x07options\x18\x07 \x01(\x0b\x32\x15.query.ExecuteOptions\"w\n\x0f\x45xecuteResponse\x12\x1e\n\x05\x65rror\x18\x01 \x01(\x0b\x32\x0f.vtrpc.RPCError\x12 \n\x07session\x18\x02 \x01(\x0b\x32\x0f.vtgate.Session\x12\"\n\x06result\x18\x03 \x01(\x0b\x32\x12.query.QueryResult\"\x8f\x02\n\x14\x45xecuteShardsRequest\x12\"\n\tcaller_id\x18\x01 \x01(\x0b\x32\x0f.vtrpc.CallerID\x12 \n\x07session\x18\x02 \x01(\x0b\x32\x0f.vtgate.Session\x12 \n\x05query\x18\x03 \x01(\x0b\x32\x11.query.BoundQuery\x12\x10\n\x08keyspace\x18\x04 \x01(\t\x12\x0e\n\x06shards\x18\x05 \x03(\t\x12)\n\x0btablet_type\x18\x06 \x01(\x0e\x32\x14.topodata.TabletType\x12\x1a\n\x12not_in_transaction\x18\x07 \x01(\x08\x12&\n\x07options\x18\x08 \x01(\x0b\x32\x15.query.ExecuteOptions\"}\n\x15\x45xecuteShardsResponse\x12\x1e\n\x05\x65rror\x18\x01 \x01(\x0b\x32\x0f.vtrpc.RPCError\x12 \n\x07session\x18\x02 \x01(\x0b\x32\x0f.vtgate.Session\x12\"\n\x06result\x18\x03 \x01(\x0b\x32\x12.query.QueryResult\"\x9a\x02\n\x19\x45xecuteKeyspaceIdsRequest\x12\"\n\tcaller_id\x18\x01 \x01(\x0b\x32\x0f.vtrpc.CallerID\x12 \n\x07session\x18\x02 \x01(\x0b\x32\x0f.vtgate.Session\x12 \n\x05query\x18\x03 \x01(\x0b\x32\x11.query.BoundQuery\x12\x10\n\x08keyspace\x18\x04 \x01(\t\x12\x14\n\x0ckeyspace_ids\x18\x05 \x03(\x0c\x12)\n\x0btablet_type\x18\x06 \x01(\x0e\x32\x14.topodata.TabletType\x12\x1a\n\x12not_in_transaction\x18\x07 \x01(\x08\x12&\n\x07options\x18\x08 \x01(\x0b\x32\x15.query.ExecuteOptions\"\x82\x01\n\x1a\x45xecuteKeyspaceIdsResponse\x12\x1e\n\x05\x65rror\x18\x01 \x01(\x0b\x32\x0f.vtrpc.RPCError\x12 \n\x07session\x18\x02 \x01(\x0b\x32\x0f.vtgate.Session\x12\"\n\x06result\x18\x03 \x01(\x0b\x32\x12.query.QueryResult\"\xaa\x02\n\x17\x45xecuteKeyRangesRequest\x12\"\n\tcaller_id\x18\x01 \x01(\x0b\x32\x0f.vtrpc.CallerID\x12 \n\x07session\x18\x02 \x01(\x0b\x32\x0f.vtgate.Session\x12 \n\x05query\x18\x03 \x01(\x0b\x32\x11.query.BoundQuery\x12\x10\n\x08keyspace\x18\x04 \x01(\t\x12&\n\nkey_ranges\x18\x05 \x03(\x0b\x32\x12.topodata.KeyRange\x12)\n\x0btablet_type\x18\x06 \x01(\x0e\x32\x14.topodata.TabletType\x12\x1a\n\x12not_in_transaction\x18\x07 \x01(\x08\x12&\n\x07options\x18\x08 \x01(\x0b\x32\x15.query.ExecuteOptions\"\x80\x01\n\x18\x45xecuteKeyRangesResponse\x12\x1e\n\x05\x65rror\x18\x01 \x01(\x0b\x32\x0f.vtrpc.RPCError\x12 \n\x07session\x18\x02 \x01(\x0b\x32\x0f.vtgate.Session\x12\"\n\x06result\x18\x03 \x01(\x0b\x32\x12.query.QueryResult\"\xb0\x03\n\x17\x45xecuteEntityIdsRequest\x12\"\n\tcaller_id\x18\x01 \x01(\x0b\x32\x0f.vtrpc.CallerID\x12 \n\x07session\x18\x02 \x01(\x0b\x32\x0f.vtgate.Session\x12 \n\x05query\x18\x03 \x01(\x0b\x32\x11.query.BoundQuery\x12\x10\n\x08keyspace\x18\x04 \x01(\t\x12\x1a\n\x12\x65ntity_column_name\x18\x05 \x01(\t\x12\x45\n\x13\x65ntity_keyspace_ids\x18\x06 \x03(\x0b\x32(.vtgate.ExecuteEntityIdsRequest.EntityId\x12)\n\x0btablet_type\x18\x07 \x01(\x0e\x32\x14.topodata.TabletType\x12\x1a\n\x12not_in_transaction\x18\x08 \x01(\x08\x12&\n\x07options\x18\t \x01(\x0b\x32\x15.query.ExecuteOptions\x1aI\n\x08\x45ntityId\x12\x19\n\x04type\x18\x01 \x01(\x0e\x32\x0b.query.Type\x12\r\n\x05value\x18\x02 \x01(\x0c\x12\x13\n\x0bkeyspace_id\x18\x03 \x01(\x0c\"\x80\x01\n\x18\x45xecuteEntityIdsResponse\x12\x1e\n\x05\x65rror\x18\x01 \x01(\x0b\x32\x0f.vtrpc.RPCError\x12 \n\x07session\x18\x02 \x01(\x0b\x32\x0f.vtgate.Session\x12\"\n\x06result\x18\x03 \x01(\x0b\x32\x12.query.QueryResult\"\xfc\x01\n\x13\x45xecuteBatchRequest\x12\"\n\tcaller_id\x18\x01 \x01(\x0b\x32\x0f.vtrpc.CallerID\x12 \n\x07session\x18\x02 \x01(\x0b\x32\x0f.vtgate.Session\x12\"\n\x07queries\x18\x03 \x03(\x0b\x32\x11.query.BoundQuery\x12)\n\x0btablet_type\x18\x04 \x01(\x0e\x32\x14.topodata.TabletType\x12\x16\n\x0e\x61s_transaction\x18\x05 \x01(\x08\x12\x10\n\x08keyspace\x18\x06 \x01(\t\x12&\n\x07options\x18\x07 \x01(\x0b\x32\x15.query.ExecuteOptions\"\x81\x01\n\x14\x45xecuteBatchResponse\x12\x1e\n\x05\x65rror\x18\x01 \x01(\x0b\x32\x0f.vtrpc.RPCError\x12 \n\x07session\x18\x02 \x01(\x0b\x32\x0f.vtgate.Session\x12\'\n\x07results\x18\x03 \x03(\x0b\x32\x16.query.ResultWithError\"U\n\x0f\x42oundShardQuery\x12 \n\x05query\x18\x01 \x01(\x0b\x32\x11.query.BoundQuery\x12\x10\n\x08keyspace\x18\x02 \x01(\t\x12\x0e\n\x06shards\x18\x03 \x03(\t\"\xf6\x01\n\x19\x45xecuteBatchShardsRequest\x12\"\n\tcaller_id\x18\x01 \x01(\x0b\x32\x0f.vtrpc.CallerID\x12 \n\x07session\x18\x02 \x01(\x0b\x32\x0f.vtgate.Session\x12(\n\x07queries\x18\x03 \x03(\x0b\x32\x17.vtgate.BoundShardQuery\x12)\n\x0btablet_type\x18\x04 \x01(\x0e\x32\x14.topodata.TabletType\x12\x16\n\x0e\x61s_transaction\x18\x05 \x01(\x08\x12&\n\x07options\x18\x06 \x01(\x0b\x32\x15.query.ExecuteOptions\"\x83\x01\n\x1a\x45xecuteBatchShardsResponse\x12\x1e\n\x05\x65rror\x18\x01 \x01(\x0b\x32\x0f.vtrpc.RPCError\x12 \n\x07session\x18\x02 \x01(\x0b\x32\x0f.vtgate.Session\x12#\n\x07results\x18\x03 \x03(\x0b\x32\x12.query.QueryResult\"`\n\x14\x42oundKeyspaceIdQuery\x12 \n\x05query\x18\x01 \x01(\x0b\x32\x11.query.BoundQuery\x12\x10\n\x08keyspace\x18\x02 \x01(\t\x12\x14\n\x0ckeyspace_ids\x18\x03 \x03(\x0c\"\x80\x02\n\x1e\x45xecuteBatchKeyspaceIdsRequest\x12\"\n\tcaller_id\x18\x01 \x01(\x0b\x32\x0f.vtrpc.CallerID\x12 \n\x07session\x18\x02 \x01(\x0b\x32\x0f.vtgate.Session\x12-\n\x07queries\x18\x03 \x03(\x0b\x32\x1c.vtgate.BoundKeyspaceIdQuery\x12)\n\x0btablet_type\x18\x04 \x01(\x0e\x32\x14.topodata.TabletType\x12\x16\n\x0e\x61s_transaction\x18\x05 \x01(\x08\x12&\n\x07options\x18\x06 \x01(\x0b\x32\x15.query.ExecuteOptions\"\x88\x01\n\x1f\x45xecuteBatchKeyspaceIdsResponse\x12\x1e\n\x05\x65rror\x18\x01 \x01(\x0b\x32\x0f.vtrpc.RPCError\x12 \n\x07session\x18\x02 \x01(\x0b\x32\x0f.vtgate.Session\x12#\n\x07results\x18\x03 \x03(\x0b\x32\x12.query.QueryResult\"\xc1\x01\n\x14StreamExecuteRequest\x12\"\n\tcaller_id\x18\x01 \x01(\x0b\x32\x0f.vtrpc.CallerID\x12 \n\x05query\x18\x02 \x01(\x0b\x32\x11.query.BoundQuery\x12)\n\x0btablet_type\x18\x03 \x01(\x0e\x32\x14.topodata.TabletType\x12\x10\n\x08keyspace\x18\x04 \x01(\t\x12&\n\x07options\x18\x05 \x01(\x0b\x32\x15.query.ExecuteOptions\";\n\x15StreamExecuteResponse\x12\"\n\x06result\x18\x01 \x01(\x0b\x32\x12.query.QueryResult\"\xd7\x01\n\x1aStreamExecuteShardsRequest\x12\"\n\tcaller_id\x18\x01 \x01(\x0b\x32\x0f.vtrpc.CallerID\x12 \n\x05query\x18\x02 \x01(\x0b\x32\x11.query.BoundQuery\x12\x10\n\x08keyspace\x18\x03 \x01(\t\x12\x0e\n\x06shards\x18\x04 \x03(\t\x12)\n\x0btablet_type\x18\x05 \x01(\x0e\x32\x14.topodata.TabletType\x12&\n\x07options\x18\x06 \x01(\x0b\x32\x15.query.ExecuteOptions\"A\n\x1bStreamExecuteShardsResponse\x12\"\n\x06result\x18\x01 \x01(\x0b\x32\x12.query.QueryResult\"\xe2\x01\n\x1fStreamExecuteKeyspaceIdsRequest\x12\"\n\tcaller_id\x18\x01 \x01(\x0b\x32\x0f.vtrpc.CallerID\x12 \n\x05query\x18\x02 \x01(\x0b\x32\x11.query.BoundQuery\x12\x10\n\x08keyspace\x18\x03 \x01(\t\x12\x14\n\x0ckeyspace_ids\x18\x04 \x03(\x0c\x12)\n\x0btablet_type\x18\x05 \x01(\x0e\x32\x14.topodata.TabletType\x12&\n\x07options\x18\x06 \x01(\x0b\x32\x15.query.ExecuteOptions\"F\n StreamExecuteKeyspaceIdsResponse\x12\"\n\x06result\x18\x01 \x01(\x0b\x32\x12.query.QueryResult\"\xf2\x01\n\x1dStreamExecuteKeyRangesRequest\x12\"\n\tcaller_id\x18\x01 \x01(\x0b\x32\x0f.vtrpc.CallerID\x12 \n\x05query\x18\x02 \x01(\x0b\x32\x11.query.BoundQuery\x12\x10\n\x08keyspace\x18\x03 \x01(\t\x12&\n\nkey_ranges\x18\x04 \x03(\x0b\x32\x12.topodata.KeyRange\x12)\n\x0btablet_type\x18\x05 \x01(\x0e\x32\x14.topodata.TabletType\x12&\n\x07options\x18\x06 \x01(\x0b\x32\x15.query.ExecuteOptions\"D\n\x1eStreamExecuteKeyRangesResponse\x12\"\n\x06result\x18\x01 \x01(\x0b\x32\x12.query.QueryResult\"E\n\x0c\x42\x65ginRequest\x12\"\n\tcaller_id\x18\x01 \x01(\x0b\x32\x0f.vtrpc.CallerID\x12\x11\n\tsingle_db\x18\x02 \x01(\x08\"1\n\rBeginResponse\x12 \n\x07session\x18\x01 \x01(\x0b\x32\x0f.vtgate.Session\"e\n\rCommitRequest\x12\"\n\tcaller_id\x18\x01 \x01(\x0b\x32\x0f.vtrpc.CallerID\x12 \n\x07session\x18\x02 \x01(\x0b\x32\x0f.vtgate.Session\x12\x0e\n\x06\x61tomic\x18\x03 \x01(\x08\"\x10\n\x0e\x43ommitResponse\"W\n\x0fRollbackRequest\x12\"\n\tcaller_id\x18\x01 \x01(\x0b\x32\x0f.vtrpc.CallerID\x12 \n\x07session\x18\x02 \x01(\x0b\x32\x0f.vtgate.Session\"\x12\n\x10RollbackResponse\"M\n\x19ResolveTransactionRequest\x12\"\n\tcaller_id\x18\x01 \x01(\x0b\x32\x0f.vtrpc.CallerID\x12\x0c\n\x04\x64tid\x18\x02 \x01(\t\"\x90\x01\n\x14MessageStreamRequest\x12\"\n\tcaller_id\x18\x01 \x01(\x0b\x32\x0f.vtrpc.CallerID\x12\x10\n\x08keyspace\x18\x02 \x01(\t\x12\r\n\x05shard\x18\x03 \x01(\t\x12%\n\tkey_range\x18\x04 \x01(\x0b\x32\x12.topodata.KeyRange\x12\x0c\n\x04name\x18\x05 \x01(\t\"r\n\x11MessageAckRequest\x12\"\n\tcaller_id\x18\x01 \x01(\x0b\x32\x0f.vtrpc.CallerID\x12\x10\n\x08keyspace\x18\x02 \x01(\t\x12\x0c\n\x04name\x18\x03 \x01(\t\x12\x19\n\x03ids\x18\x04 \x03(\x0b\x32\x0c.query.Value\"\x1c\n\x1aResolveTransactionResponse\"\x8a\x02\n\x11SplitQueryRequest\x12\"\n\tcaller_id\x18\x01 \x01(\x0b\x32\x0f.vtrpc.CallerID\x12\x10\n\x08keyspace\x18\x02 \x01(\t\x12 \n\x05query\x18\x03 \x01(\x0b\x32\x11.query.BoundQuery\x12\x14\n\x0csplit_column\x18\x04 \x03(\t\x12\x13\n\x0bsplit_count\x18\x05 \x01(\x03\x12\x1f\n\x17num_rows_per_query_part\x18\x06 \x01(\x03\x12\x35\n\talgorithm\x18\x07 \x01(\x0e\x32\".query.SplitQueryRequest.Algorithm\x12\x1a\n\x12use_split_query_v2\x18\x08 \x01(\x08\"\xf2\x02\n\x12SplitQueryResponse\x12/\n\x06splits\x18\x01 \x03(\x0b\x32\x1f.vtgate.SplitQueryResponse.Part\x1aH\n\x0cKeyRangePart\x12\x10\n\x08keyspace\x18\x01 \x01(\t\x12&\n\nkey_ranges\x18\x02 \x03(\x0b\x32\x12.topodata.KeyRange\x1a-\n\tShardPart\x12\x10\n\x08keyspace\x18\x01 \x01(\t\x12\x0e\n\x06shards\x18\x02 \x03(\t\x1a\xb1\x01\n\x04Part\x12 \n\x05query\x18\x01 \x01(\x0b\x32\x11.query.BoundQuery\x12?\n\x0ekey_range_part\x18\x02 \x01(\x0b\x32\'.vtgate.SplitQueryResponse.KeyRangePart\x12\x38\n\nshard_part\x18\x03 \x01(\x0b\x32$.vtgate.SplitQueryResponse.ShardPart\x12\x0c\n\x04size\x18\x04 \x01(\x03\")\n\x15GetSrvKeyspaceRequest\x12\x10\n\x08keyspace\x18\x01 \x01(\t\"E\n\x16GetSrvKeyspaceResponse\x12+\n\x0csrv_keyspace\x18\x01 \x01(\x0b\x32\x15.topodata.SrvKeyspace\"\xe1\x01\n\x13UpdateStreamRequest\x12\"\n\tcaller_id\x18\x01 \x01(\x0b\x32\x0f.vtrpc.CallerID\x12\x10\n\x08keyspace\x18\x02 \x01(\t\x12\r\n\x05shard\x18\x03 \x01(\t\x12%\n\tkey_range\x18\x04 \x01(\x0b\x32\x12.topodata.KeyRange\x12)\n\x0btablet_type\x18\x05 \x01(\x0e\x32\x14.topodata.TabletType\x12\x11\n\ttimestamp\x18\x06 \x01(\x03\x12 \n\x05\x65vent\x18\x07 \x01(\x0b\x32\x11.query.EventToken\"S\n\x14UpdateStreamResponse\x12!\n\x05\x65vent\x18\x01 \x01(\x0b\x32\x12.query.StreamEvent\x12\x18\n\x10resume_timestamp\x18\x02 \x01(\x03\x42\x1a\n\x18\x63om.youtube.vitess.protob\x06proto3') + serialized_pb=_b('\n\x0cvtgate.proto\x12\x06vtgate\x1a\x0bquery.proto\x1a\x0etopodata.proto\x1a\x0bvtrpc.proto\"\xc5\x01\n\x07Session\x12\x16\n\x0ein_transaction\x18\x01 \x01(\x08\x12\x34\n\x0eshard_sessions\x18\x02 \x03(\x0b\x32\x1c.vtgate.Session.ShardSession\x12\x11\n\tsingle_db\x18\x03 \x01(\x08\x12\x12\n\nautocommit\x18\x04 \x01(\x08\x1a\x45\n\x0cShardSession\x12\x1d\n\x06target\x18\x01 \x01(\x0b\x32\r.query.Target\x12\x16\n\x0etransaction_id\x18\x02 \x01(\x03\"\xff\x01\n\x0e\x45xecuteRequest\x12\"\n\tcaller_id\x18\x01 \x01(\x0b\x32\x0f.vtrpc.CallerID\x12 \n\x07session\x18\x02 \x01(\x0b\x32\x0f.vtgate.Session\x12 \n\x05query\x18\x03 \x01(\x0b\x32\x11.query.BoundQuery\x12)\n\x0btablet_type\x18\x04 \x01(\x0e\x32\x14.topodata.TabletType\x12\x1a\n\x12not_in_transaction\x18\x05 \x01(\x08\x12\x16\n\x0ekeyspace_shard\x18\x06 \x01(\t\x12&\n\x07options\x18\x07 \x01(\x0b\x32\x15.query.ExecuteOptions\"w\n\x0f\x45xecuteResponse\x12\x1e\n\x05\x65rror\x18\x01 \x01(\x0b\x32\x0f.vtrpc.RPCError\x12 \n\x07session\x18\x02 \x01(\x0b\x32\x0f.vtgate.Session\x12\"\n\x06result\x18\x03 \x01(\x0b\x32\x12.query.QueryResult\"\x8f\x02\n\x14\x45xecuteShardsRequest\x12\"\n\tcaller_id\x18\x01 \x01(\x0b\x32\x0f.vtrpc.CallerID\x12 \n\x07session\x18\x02 \x01(\x0b\x32\x0f.vtgate.Session\x12 \n\x05query\x18\x03 \x01(\x0b\x32\x11.query.BoundQuery\x12\x10\n\x08keyspace\x18\x04 \x01(\t\x12\x0e\n\x06shards\x18\x05 \x03(\t\x12)\n\x0btablet_type\x18\x06 \x01(\x0e\x32\x14.topodata.TabletType\x12\x1a\n\x12not_in_transaction\x18\x07 \x01(\x08\x12&\n\x07options\x18\x08 \x01(\x0b\x32\x15.query.ExecuteOptions\"}\n\x15\x45xecuteShardsResponse\x12\x1e\n\x05\x65rror\x18\x01 \x01(\x0b\x32\x0f.vtrpc.RPCError\x12 \n\x07session\x18\x02 \x01(\x0b\x32\x0f.vtgate.Session\x12\"\n\x06result\x18\x03 \x01(\x0b\x32\x12.query.QueryResult\"\x9a\x02\n\x19\x45xecuteKeyspaceIdsRequest\x12\"\n\tcaller_id\x18\x01 \x01(\x0b\x32\x0f.vtrpc.CallerID\x12 \n\x07session\x18\x02 \x01(\x0b\x32\x0f.vtgate.Session\x12 \n\x05query\x18\x03 \x01(\x0b\x32\x11.query.BoundQuery\x12\x10\n\x08keyspace\x18\x04 \x01(\t\x12\x14\n\x0ckeyspace_ids\x18\x05 \x03(\x0c\x12)\n\x0btablet_type\x18\x06 \x01(\x0e\x32\x14.topodata.TabletType\x12\x1a\n\x12not_in_transaction\x18\x07 \x01(\x08\x12&\n\x07options\x18\x08 \x01(\x0b\x32\x15.query.ExecuteOptions\"\x82\x01\n\x1a\x45xecuteKeyspaceIdsResponse\x12\x1e\n\x05\x65rror\x18\x01 \x01(\x0b\x32\x0f.vtrpc.RPCError\x12 \n\x07session\x18\x02 \x01(\x0b\x32\x0f.vtgate.Session\x12\"\n\x06result\x18\x03 \x01(\x0b\x32\x12.query.QueryResult\"\xaa\x02\n\x17\x45xecuteKeyRangesRequest\x12\"\n\tcaller_id\x18\x01 \x01(\x0b\x32\x0f.vtrpc.CallerID\x12 \n\x07session\x18\x02 \x01(\x0b\x32\x0f.vtgate.Session\x12 \n\x05query\x18\x03 \x01(\x0b\x32\x11.query.BoundQuery\x12\x10\n\x08keyspace\x18\x04 \x01(\t\x12&\n\nkey_ranges\x18\x05 \x03(\x0b\x32\x12.topodata.KeyRange\x12)\n\x0btablet_type\x18\x06 \x01(\x0e\x32\x14.topodata.TabletType\x12\x1a\n\x12not_in_transaction\x18\x07 \x01(\x08\x12&\n\x07options\x18\x08 \x01(\x0b\x32\x15.query.ExecuteOptions\"\x80\x01\n\x18\x45xecuteKeyRangesResponse\x12\x1e\n\x05\x65rror\x18\x01 \x01(\x0b\x32\x0f.vtrpc.RPCError\x12 \n\x07session\x18\x02 \x01(\x0b\x32\x0f.vtgate.Session\x12\"\n\x06result\x18\x03 \x01(\x0b\x32\x12.query.QueryResult\"\xb0\x03\n\x17\x45xecuteEntityIdsRequest\x12\"\n\tcaller_id\x18\x01 \x01(\x0b\x32\x0f.vtrpc.CallerID\x12 \n\x07session\x18\x02 \x01(\x0b\x32\x0f.vtgate.Session\x12 \n\x05query\x18\x03 \x01(\x0b\x32\x11.query.BoundQuery\x12\x10\n\x08keyspace\x18\x04 \x01(\t\x12\x1a\n\x12\x65ntity_column_name\x18\x05 \x01(\t\x12\x45\n\x13\x65ntity_keyspace_ids\x18\x06 \x03(\x0b\x32(.vtgate.ExecuteEntityIdsRequest.EntityId\x12)\n\x0btablet_type\x18\x07 \x01(\x0e\x32\x14.topodata.TabletType\x12\x1a\n\x12not_in_transaction\x18\x08 \x01(\x08\x12&\n\x07options\x18\t \x01(\x0b\x32\x15.query.ExecuteOptions\x1aI\n\x08\x45ntityId\x12\x19\n\x04type\x18\x01 \x01(\x0e\x32\x0b.query.Type\x12\r\n\x05value\x18\x02 \x01(\x0c\x12\x13\n\x0bkeyspace_id\x18\x03 \x01(\x0c\"\x80\x01\n\x18\x45xecuteEntityIdsResponse\x12\x1e\n\x05\x65rror\x18\x01 \x01(\x0b\x32\x0f.vtrpc.RPCError\x12 \n\x07session\x18\x02 \x01(\x0b\x32\x0f.vtgate.Session\x12\"\n\x06result\x18\x03 \x01(\x0b\x32\x12.query.QueryResult\"\x82\x02\n\x13\x45xecuteBatchRequest\x12\"\n\tcaller_id\x18\x01 \x01(\x0b\x32\x0f.vtrpc.CallerID\x12 \n\x07session\x18\x02 \x01(\x0b\x32\x0f.vtgate.Session\x12\"\n\x07queries\x18\x03 \x03(\x0b\x32\x11.query.BoundQuery\x12)\n\x0btablet_type\x18\x04 \x01(\x0e\x32\x14.topodata.TabletType\x12\x16\n\x0e\x61s_transaction\x18\x05 \x01(\x08\x12\x16\n\x0ekeyspace_shard\x18\x06 \x01(\t\x12&\n\x07options\x18\x07 \x01(\x0b\x32\x15.query.ExecuteOptions\"\x81\x01\n\x14\x45xecuteBatchResponse\x12\x1e\n\x05\x65rror\x18\x01 \x01(\x0b\x32\x0f.vtrpc.RPCError\x12 \n\x07session\x18\x02 \x01(\x0b\x32\x0f.vtgate.Session\x12\'\n\x07results\x18\x03 \x03(\x0b\x32\x16.query.ResultWithError\"U\n\x0f\x42oundShardQuery\x12 \n\x05query\x18\x01 \x01(\x0b\x32\x11.query.BoundQuery\x12\x10\n\x08keyspace\x18\x02 \x01(\t\x12\x0e\n\x06shards\x18\x03 \x03(\t\"\xf6\x01\n\x19\x45xecuteBatchShardsRequest\x12\"\n\tcaller_id\x18\x01 \x01(\x0b\x32\x0f.vtrpc.CallerID\x12 \n\x07session\x18\x02 \x01(\x0b\x32\x0f.vtgate.Session\x12(\n\x07queries\x18\x03 \x03(\x0b\x32\x17.vtgate.BoundShardQuery\x12)\n\x0btablet_type\x18\x04 \x01(\x0e\x32\x14.topodata.TabletType\x12\x16\n\x0e\x61s_transaction\x18\x05 \x01(\x08\x12&\n\x07options\x18\x06 \x01(\x0b\x32\x15.query.ExecuteOptions\"\x83\x01\n\x1a\x45xecuteBatchShardsResponse\x12\x1e\n\x05\x65rror\x18\x01 \x01(\x0b\x32\x0f.vtrpc.RPCError\x12 \n\x07session\x18\x02 \x01(\x0b\x32\x0f.vtgate.Session\x12#\n\x07results\x18\x03 \x03(\x0b\x32\x12.query.QueryResult\"`\n\x14\x42oundKeyspaceIdQuery\x12 \n\x05query\x18\x01 \x01(\x0b\x32\x11.query.BoundQuery\x12\x10\n\x08keyspace\x18\x02 \x01(\t\x12\x14\n\x0ckeyspace_ids\x18\x03 \x03(\x0c\"\x80\x02\n\x1e\x45xecuteBatchKeyspaceIdsRequest\x12\"\n\tcaller_id\x18\x01 \x01(\x0b\x32\x0f.vtrpc.CallerID\x12 \n\x07session\x18\x02 \x01(\x0b\x32\x0f.vtgate.Session\x12-\n\x07queries\x18\x03 \x03(\x0b\x32\x1c.vtgate.BoundKeyspaceIdQuery\x12)\n\x0btablet_type\x18\x04 \x01(\x0e\x32\x14.topodata.TabletType\x12\x16\n\x0e\x61s_transaction\x18\x05 \x01(\x08\x12&\n\x07options\x18\x06 \x01(\x0b\x32\x15.query.ExecuteOptions\"\x88\x01\n\x1f\x45xecuteBatchKeyspaceIdsResponse\x12\x1e\n\x05\x65rror\x18\x01 \x01(\x0b\x32\x0f.vtrpc.RPCError\x12 \n\x07session\x18\x02 \x01(\x0b\x32\x0f.vtgate.Session\x12#\n\x07results\x18\x03 \x03(\x0b\x32\x12.query.QueryResult\"\xc7\x01\n\x14StreamExecuteRequest\x12\"\n\tcaller_id\x18\x01 \x01(\x0b\x32\x0f.vtrpc.CallerID\x12 \n\x05query\x18\x02 \x01(\x0b\x32\x11.query.BoundQuery\x12)\n\x0btablet_type\x18\x03 \x01(\x0e\x32\x14.topodata.TabletType\x12\x16\n\x0ekeyspace_shard\x18\x04 \x01(\t\x12&\n\x07options\x18\x05 \x01(\x0b\x32\x15.query.ExecuteOptions\";\n\x15StreamExecuteResponse\x12\"\n\x06result\x18\x01 \x01(\x0b\x32\x12.query.QueryResult\"\xd7\x01\n\x1aStreamExecuteShardsRequest\x12\"\n\tcaller_id\x18\x01 \x01(\x0b\x32\x0f.vtrpc.CallerID\x12 \n\x05query\x18\x02 \x01(\x0b\x32\x11.query.BoundQuery\x12\x10\n\x08keyspace\x18\x03 \x01(\t\x12\x0e\n\x06shards\x18\x04 \x03(\t\x12)\n\x0btablet_type\x18\x05 \x01(\x0e\x32\x14.topodata.TabletType\x12&\n\x07options\x18\x06 \x01(\x0b\x32\x15.query.ExecuteOptions\"A\n\x1bStreamExecuteShardsResponse\x12\"\n\x06result\x18\x01 \x01(\x0b\x32\x12.query.QueryResult\"\xe2\x01\n\x1fStreamExecuteKeyspaceIdsRequest\x12\"\n\tcaller_id\x18\x01 \x01(\x0b\x32\x0f.vtrpc.CallerID\x12 \n\x05query\x18\x02 \x01(\x0b\x32\x11.query.BoundQuery\x12\x10\n\x08keyspace\x18\x03 \x01(\t\x12\x14\n\x0ckeyspace_ids\x18\x04 \x03(\x0c\x12)\n\x0btablet_type\x18\x05 \x01(\x0e\x32\x14.topodata.TabletType\x12&\n\x07options\x18\x06 \x01(\x0b\x32\x15.query.ExecuteOptions\"F\n StreamExecuteKeyspaceIdsResponse\x12\"\n\x06result\x18\x01 \x01(\x0b\x32\x12.query.QueryResult\"\xf2\x01\n\x1dStreamExecuteKeyRangesRequest\x12\"\n\tcaller_id\x18\x01 \x01(\x0b\x32\x0f.vtrpc.CallerID\x12 \n\x05query\x18\x02 \x01(\x0b\x32\x11.query.BoundQuery\x12\x10\n\x08keyspace\x18\x03 \x01(\t\x12&\n\nkey_ranges\x18\x04 \x03(\x0b\x32\x12.topodata.KeyRange\x12)\n\x0btablet_type\x18\x05 \x01(\x0e\x32\x14.topodata.TabletType\x12&\n\x07options\x18\x06 \x01(\x0b\x32\x15.query.ExecuteOptions\"D\n\x1eStreamExecuteKeyRangesResponse\x12\"\n\x06result\x18\x01 \x01(\x0b\x32\x12.query.QueryResult\"E\n\x0c\x42\x65ginRequest\x12\"\n\tcaller_id\x18\x01 \x01(\x0b\x32\x0f.vtrpc.CallerID\x12\x11\n\tsingle_db\x18\x02 \x01(\x08\"1\n\rBeginResponse\x12 \n\x07session\x18\x01 \x01(\x0b\x32\x0f.vtgate.Session\"e\n\rCommitRequest\x12\"\n\tcaller_id\x18\x01 \x01(\x0b\x32\x0f.vtrpc.CallerID\x12 \n\x07session\x18\x02 \x01(\x0b\x32\x0f.vtgate.Session\x12\x0e\n\x06\x61tomic\x18\x03 \x01(\x08\"\x10\n\x0e\x43ommitResponse\"W\n\x0fRollbackRequest\x12\"\n\tcaller_id\x18\x01 \x01(\x0b\x32\x0f.vtrpc.CallerID\x12 \n\x07session\x18\x02 \x01(\x0b\x32\x0f.vtgate.Session\"\x12\n\x10RollbackResponse\"M\n\x19ResolveTransactionRequest\x12\"\n\tcaller_id\x18\x01 \x01(\x0b\x32\x0f.vtrpc.CallerID\x12\x0c\n\x04\x64tid\x18\x02 \x01(\t\"\x90\x01\n\x14MessageStreamRequest\x12\"\n\tcaller_id\x18\x01 \x01(\x0b\x32\x0f.vtrpc.CallerID\x12\x10\n\x08keyspace\x18\x02 \x01(\t\x12\r\n\x05shard\x18\x03 \x01(\t\x12%\n\tkey_range\x18\x04 \x01(\x0b\x32\x12.topodata.KeyRange\x12\x0c\n\x04name\x18\x05 \x01(\t\"r\n\x11MessageAckRequest\x12\"\n\tcaller_id\x18\x01 \x01(\x0b\x32\x0f.vtrpc.CallerID\x12\x10\n\x08keyspace\x18\x02 \x01(\t\x12\x0c\n\x04name\x18\x03 \x01(\t\x12\x19\n\x03ids\x18\x04 \x03(\x0b\x32\x0c.query.Value\"\x1c\n\x1aResolveTransactionResponse\"\x8a\x02\n\x11SplitQueryRequest\x12\"\n\tcaller_id\x18\x01 \x01(\x0b\x32\x0f.vtrpc.CallerID\x12\x10\n\x08keyspace\x18\x02 \x01(\t\x12 \n\x05query\x18\x03 \x01(\x0b\x32\x11.query.BoundQuery\x12\x14\n\x0csplit_column\x18\x04 \x03(\t\x12\x13\n\x0bsplit_count\x18\x05 \x01(\x03\x12\x1f\n\x17num_rows_per_query_part\x18\x06 \x01(\x03\x12\x35\n\talgorithm\x18\x07 \x01(\x0e\x32\".query.SplitQueryRequest.Algorithm\x12\x1a\n\x12use_split_query_v2\x18\x08 \x01(\x08\"\xf2\x02\n\x12SplitQueryResponse\x12/\n\x06splits\x18\x01 \x03(\x0b\x32\x1f.vtgate.SplitQueryResponse.Part\x1aH\n\x0cKeyRangePart\x12\x10\n\x08keyspace\x18\x01 \x01(\t\x12&\n\nkey_ranges\x18\x02 \x03(\x0b\x32\x12.topodata.KeyRange\x1a-\n\tShardPart\x12\x10\n\x08keyspace\x18\x01 \x01(\t\x12\x0e\n\x06shards\x18\x02 \x03(\t\x1a\xb1\x01\n\x04Part\x12 \n\x05query\x18\x01 \x01(\x0b\x32\x11.query.BoundQuery\x12?\n\x0ekey_range_part\x18\x02 \x01(\x0b\x32\'.vtgate.SplitQueryResponse.KeyRangePart\x12\x38\n\nshard_part\x18\x03 \x01(\x0b\x32$.vtgate.SplitQueryResponse.ShardPart\x12\x0c\n\x04size\x18\x04 \x01(\x03\")\n\x15GetSrvKeyspaceRequest\x12\x10\n\x08keyspace\x18\x01 \x01(\t\"E\n\x16GetSrvKeyspaceResponse\x12+\n\x0csrv_keyspace\x18\x01 \x01(\x0b\x32\x15.topodata.SrvKeyspace\"\xe1\x01\n\x13UpdateStreamRequest\x12\"\n\tcaller_id\x18\x01 \x01(\x0b\x32\x0f.vtrpc.CallerID\x12\x10\n\x08keyspace\x18\x02 \x01(\t\x12\r\n\x05shard\x18\x03 \x01(\t\x12%\n\tkey_range\x18\x04 \x01(\x0b\x32\x12.topodata.KeyRange\x12)\n\x0btablet_type\x18\x05 \x01(\x0e\x32\x14.topodata.TabletType\x12\x11\n\ttimestamp\x18\x06 \x01(\x03\x12 \n\x05\x65vent\x18\x07 \x01(\x0b\x32\x11.query.EventToken\"S\n\x14UpdateStreamResponse\x12!\n\x05\x65vent\x18\x01 \x01(\x0b\x32\x12.query.StreamEvent\x12\x18\n\x10resume_timestamp\x18\x02 \x01(\x03\x42\x1a\n\x18\x63om.youtube.vitess.protob\x06proto3') , dependencies=[query__pb2.DESCRIPTOR,topodata__pb2.DESCRIPTOR,vtrpc__pb2.DESCRIPTOR,]) _sym_db.RegisterFileDescriptor(DESCRIPTOR) @@ -63,8 +63,8 @@ extension_ranges=[], oneofs=[ ], - serialized_start=175, - serialized_end=244, + serialized_start=195, + serialized_end=264, ) _SESSION = _descriptor.Descriptor( @@ -95,6 +95,13 @@ message_type=None, enum_type=None, containing_type=None, is_extension=False, extension_scope=None, options=None), + _descriptor.FieldDescriptor( + name='autocommit', full_name='vtgate.Session.autocommit', index=3, + number=4, type=8, cpp_type=7, label=1, + has_default_value=False, default_value=False, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None), ], extensions=[ ], @@ -108,7 +115,7 @@ oneofs=[ ], serialized_start=67, - serialized_end=244, + serialized_end=264, ) @@ -155,7 +162,7 @@ is_extension=False, extension_scope=None, options=None), _descriptor.FieldDescriptor( - name='keyspace', full_name='vtgate.ExecuteRequest.keyspace', index=5, + name='keyspace_shard', full_name='vtgate.ExecuteRequest.keyspace_shard', index=5, number=6, type=9, cpp_type=9, label=1, has_default_value=False, default_value=_b("").decode('utf-8'), message_type=None, enum_type=None, containing_type=None, @@ -180,8 +187,8 @@ extension_ranges=[], oneofs=[ ], - serialized_start=247, - serialized_end=496, + serialized_start=267, + serialized_end=522, ) @@ -225,8 +232,8 @@ extension_ranges=[], oneofs=[ ], - serialized_start=498, - serialized_end=617, + serialized_start=524, + serialized_end=643, ) @@ -305,8 +312,8 @@ extension_ranges=[], oneofs=[ ], - serialized_start=620, - serialized_end=891, + serialized_start=646, + serialized_end=917, ) @@ -350,8 +357,8 @@ extension_ranges=[], oneofs=[ ], - serialized_start=893, - serialized_end=1018, + serialized_start=919, + serialized_end=1044, ) @@ -430,8 +437,8 @@ extension_ranges=[], oneofs=[ ], - serialized_start=1021, - serialized_end=1303, + serialized_start=1047, + serialized_end=1329, ) @@ -475,8 +482,8 @@ extension_ranges=[], oneofs=[ ], - serialized_start=1306, - serialized_end=1436, + serialized_start=1332, + serialized_end=1462, ) @@ -555,8 +562,8 @@ extension_ranges=[], oneofs=[ ], - serialized_start=1439, - serialized_end=1737, + serialized_start=1465, + serialized_end=1763, ) @@ -600,8 +607,8 @@ extension_ranges=[], oneofs=[ ], - serialized_start=1740, - serialized_end=1868, + serialized_start=1766, + serialized_end=1894, ) @@ -645,8 +652,8 @@ extension_ranges=[], oneofs=[ ], - serialized_start=2230, - serialized_end=2303, + serialized_start=2256, + serialized_end=2329, ) _EXECUTEENTITYIDSREQUEST = _descriptor.Descriptor( @@ -731,8 +738,8 @@ extension_ranges=[], oneofs=[ ], - serialized_start=1871, - serialized_end=2303, + serialized_start=1897, + serialized_end=2329, ) @@ -776,8 +783,8 @@ extension_ranges=[], oneofs=[ ], - serialized_start=2306, - serialized_end=2434, + serialized_start=2332, + serialized_end=2460, ) @@ -824,7 +831,7 @@ is_extension=False, extension_scope=None, options=None), _descriptor.FieldDescriptor( - name='keyspace', full_name='vtgate.ExecuteBatchRequest.keyspace', index=5, + name='keyspace_shard', full_name='vtgate.ExecuteBatchRequest.keyspace_shard', index=5, number=6, type=9, cpp_type=9, label=1, has_default_value=False, default_value=_b("").decode('utf-8'), message_type=None, enum_type=None, containing_type=None, @@ -849,8 +856,8 @@ extension_ranges=[], oneofs=[ ], - serialized_start=2437, - serialized_end=2689, + serialized_start=2463, + serialized_end=2721, ) @@ -894,8 +901,8 @@ extension_ranges=[], oneofs=[ ], - serialized_start=2692, - serialized_end=2821, + serialized_start=2724, + serialized_end=2853, ) @@ -939,8 +946,8 @@ extension_ranges=[], oneofs=[ ], - serialized_start=2823, - serialized_end=2908, + serialized_start=2855, + serialized_end=2940, ) @@ -1005,8 +1012,8 @@ extension_ranges=[], oneofs=[ ], - serialized_start=2911, - serialized_end=3157, + serialized_start=2943, + serialized_end=3189, ) @@ -1050,8 +1057,8 @@ extension_ranges=[], oneofs=[ ], - serialized_start=3160, - serialized_end=3291, + serialized_start=3192, + serialized_end=3323, ) @@ -1095,8 +1102,8 @@ extension_ranges=[], oneofs=[ ], - serialized_start=3293, - serialized_end=3389, + serialized_start=3325, + serialized_end=3421, ) @@ -1161,8 +1168,8 @@ extension_ranges=[], oneofs=[ ], - serialized_start=3392, - serialized_end=3648, + serialized_start=3424, + serialized_end=3680, ) @@ -1206,8 +1213,8 @@ extension_ranges=[], oneofs=[ ], - serialized_start=3651, - serialized_end=3787, + serialized_start=3683, + serialized_end=3819, ) @@ -1240,7 +1247,7 @@ is_extension=False, extension_scope=None, options=None), _descriptor.FieldDescriptor( - name='keyspace', full_name='vtgate.StreamExecuteRequest.keyspace', index=3, + name='keyspace_shard', full_name='vtgate.StreamExecuteRequest.keyspace_shard', index=3, number=4, type=9, cpp_type=9, label=1, has_default_value=False, default_value=_b("").decode('utf-8'), message_type=None, enum_type=None, containing_type=None, @@ -1265,8 +1272,8 @@ extension_ranges=[], oneofs=[ ], - serialized_start=3790, - serialized_end=3983, + serialized_start=3822, + serialized_end=4021, ) @@ -1296,8 +1303,8 @@ extension_ranges=[], oneofs=[ ], - serialized_start=3985, - serialized_end=4044, + serialized_start=4023, + serialized_end=4082, ) @@ -1362,8 +1369,8 @@ extension_ranges=[], oneofs=[ ], - serialized_start=4047, - serialized_end=4262, + serialized_start=4085, + serialized_end=4300, ) @@ -1393,8 +1400,8 @@ extension_ranges=[], oneofs=[ ], - serialized_start=4264, - serialized_end=4329, + serialized_start=4302, + serialized_end=4367, ) @@ -1459,8 +1466,8 @@ extension_ranges=[], oneofs=[ ], - serialized_start=4332, - serialized_end=4558, + serialized_start=4370, + serialized_end=4596, ) @@ -1490,8 +1497,8 @@ extension_ranges=[], oneofs=[ ], - serialized_start=4560, - serialized_end=4630, + serialized_start=4598, + serialized_end=4668, ) @@ -1556,8 +1563,8 @@ extension_ranges=[], oneofs=[ ], - serialized_start=4633, - serialized_end=4875, + serialized_start=4671, + serialized_end=4913, ) @@ -1587,8 +1594,8 @@ extension_ranges=[], oneofs=[ ], - serialized_start=4877, - serialized_end=4945, + serialized_start=4915, + serialized_end=4983, ) @@ -1625,8 +1632,8 @@ extension_ranges=[], oneofs=[ ], - serialized_start=4947, - serialized_end=5016, + serialized_start=4985, + serialized_end=5054, ) @@ -1656,8 +1663,8 @@ extension_ranges=[], oneofs=[ ], - serialized_start=5018, - serialized_end=5067, + serialized_start=5056, + serialized_end=5105, ) @@ -1701,8 +1708,8 @@ extension_ranges=[], oneofs=[ ], - serialized_start=5069, - serialized_end=5170, + serialized_start=5107, + serialized_end=5208, ) @@ -1725,8 +1732,8 @@ extension_ranges=[], oneofs=[ ], - serialized_start=5172, - serialized_end=5188, + serialized_start=5210, + serialized_end=5226, ) @@ -1763,8 +1770,8 @@ extension_ranges=[], oneofs=[ ], - serialized_start=5190, - serialized_end=5277, + serialized_start=5228, + serialized_end=5315, ) @@ -1787,8 +1794,8 @@ extension_ranges=[], oneofs=[ ], - serialized_start=5279, - serialized_end=5297, + serialized_start=5317, + serialized_end=5335, ) @@ -1825,8 +1832,8 @@ extension_ranges=[], oneofs=[ ], - serialized_start=5299, - serialized_end=5376, + serialized_start=5337, + serialized_end=5414, ) @@ -1884,8 +1891,8 @@ extension_ranges=[], oneofs=[ ], - serialized_start=5379, - serialized_end=5523, + serialized_start=5417, + serialized_end=5561, ) @@ -1936,8 +1943,8 @@ extension_ranges=[], oneofs=[ ], - serialized_start=5525, - serialized_end=5639, + serialized_start=5563, + serialized_end=5677, ) @@ -1960,8 +1967,8 @@ extension_ranges=[], oneofs=[ ], - serialized_start=5641, - serialized_end=5669, + serialized_start=5679, + serialized_end=5707, ) @@ -2040,8 +2047,8 @@ extension_ranges=[], oneofs=[ ], - serialized_start=5672, - serialized_end=5938, + serialized_start=5710, + serialized_end=5976, ) @@ -2078,8 +2085,8 @@ extension_ranges=[], oneofs=[ ], - serialized_start=6012, - serialized_end=6084, + serialized_start=6050, + serialized_end=6122, ) _SPLITQUERYRESPONSE_SHARDPART = _descriptor.Descriptor( @@ -2115,8 +2122,8 @@ extension_ranges=[], oneofs=[ ], - serialized_start=6086, - serialized_end=6131, + serialized_start=6124, + serialized_end=6169, ) _SPLITQUERYRESPONSE_PART = _descriptor.Descriptor( @@ -2166,8 +2173,8 @@ extension_ranges=[], oneofs=[ ], - serialized_start=6134, - serialized_end=6311, + serialized_start=6172, + serialized_end=6349, ) _SPLITQUERYRESPONSE = _descriptor.Descriptor( @@ -2196,8 +2203,8 @@ extension_ranges=[], oneofs=[ ], - serialized_start=5941, - serialized_end=6311, + serialized_start=5979, + serialized_end=6349, ) @@ -2227,8 +2234,8 @@ extension_ranges=[], oneofs=[ ], - serialized_start=6313, - serialized_end=6354, + serialized_start=6351, + serialized_end=6392, ) @@ -2258,8 +2265,8 @@ extension_ranges=[], oneofs=[ ], - serialized_start=6356, - serialized_end=6425, + serialized_start=6394, + serialized_end=6463, ) @@ -2331,8 +2338,8 @@ extension_ranges=[], oneofs=[ ], - serialized_start=6428, - serialized_end=6653, + serialized_start=6466, + serialized_end=6691, ) @@ -2369,8 +2376,8 @@ extension_ranges=[], oneofs=[ ], - serialized_start=6655, - serialized_end=6738, + serialized_start=6693, + serialized_end=6776, ) _SESSION_SHARDSESSION.fields_by_name['target'].message_type = query__pb2._TARGET diff --git a/py/vttest/environment.py b/py/vttest/environment.py index aeaf0d5b740..e24ad71842b 100644 --- a/py/vttest/environment.py +++ b/py/vttest/environment.py @@ -92,13 +92,13 @@ def get_port(name, protocol=None): ValueError: the port name is invalid. """ if name == 'vtcombo': - port = base_port + if protocol == 'grpc': + # We can't use the base_port for grpc. + return base_port + 1 + return base_port elif name == 'mysql': - port = base_port + 2 + return base_port + 2 + elif name == 'vtcombo_mysql_port': + return base_port + 3 else: raise ValueError('name should be vtcombo or mysql, not %s' % name) - - if protocol == 'grpc': - port += 1 - - return port diff --git a/py/vttest/local_database.py b/py/vttest/local_database.py index 62c9236200d..66c24b10f22 100644 --- a/py/vttest/local_database.py +++ b/py/vttest/local_database.py @@ -113,6 +113,7 @@ def config(self): result = { 'port': vt_processes.vtcombo_process.port, 'socket': self.mysql_db.unix_socket(), + 'vtcombo_mysql_port': vt_processes.vtcombo_process.vtcombo_mysql_port, } if environment.get_protocol() == 'grpc': diff --git a/py/vttest/mysql_flavor.py b/py/vttest/mysql_flavor.py index 37b17c2c337..ddd5e2b5f4a 100644 --- a/py/vttest/mysql_flavor.py +++ b/py/vttest/mysql_flavor.py @@ -4,7 +4,6 @@ between various flavors of mysql. """ -import environment import logging import os import sys diff --git a/py/vttest/vt_processes.py b/py/vttest/vt_processes.py index b80b6ccff37..5853ef29eaf 100644 --- a/py/vttest/vt_processes.py +++ b/py/vttest/vt_processes.py @@ -156,6 +156,10 @@ def __init__(self, directory, topology, mysql_db, schema_dir, charset, '-db-config-app-port', str(mysql_db.port()), '-db-config-dba-host', mysql_db.hostname(), '-db-config-dba-port', str(mysql_db.port())]) + self.vtcombo_mysql_port = environment.get_port('vtcombo_mysql_port') + self.extraparams.extend( + ['-mysql_auth_server_impl', 'none', + '-mysql_server_port', str(self.vtcombo_mysql_port)]) vtcombo_process = None diff --git a/test/base_sharding.py b/test/base_sharding.py index 2a6ce34b1d0..e3cb51a6873 100644 --- a/test/base_sharding.py +++ b/test/base_sharding.py @@ -17,6 +17,7 @@ keyspace_id_type = keyrange_constants.KIT_UINT64 +use_rbr = False pack_keyspace_id = struct.Struct('!Q').pack # fixed_parent_id is used as fixed value for the "parent_id" column in all rows. diff --git a/test/cluster/keytar/Dockerfile b/test/cluster/keytar/Dockerfile new file mode 100644 index 00000000000..06456cd6b7e --- /dev/null +++ b/test/cluster/keytar/Dockerfile @@ -0,0 +1,43 @@ +# Dockerfile for generating the keytar image. See README.md for more information. +FROM debian:jessie + +ENV DEBIAN_FRONTEND noninteractive + +RUN apt-get update -y \ + && apt-get install --no-install-recommends -y -q \ + apt-utils \ + apt-transport-https \ + build-essential \ + curl \ + python2.7 \ + python2.7-dev \ + python-pip \ + git \ + wget \ + && pip install -U pip \ + && pip install virtualenv + +RUN echo "deb https://packages.cloud.google.com/apt cloud-sdk-jessie main" | tee -a /etc/apt/sources.list.d/google-cloud-sdk.list +RUN curl https://packages.cloud.google.com/apt/doc/apt-key.gpg | apt-key add - +RUN apt-get update -y && apt-get install -y google-cloud-sdk && apt-get install -y kubectl + +WORKDIR /app +RUN virtualenv /env +ADD requirements.txt /app/requirements.txt +RUN /env/bin/pip install -r /app/requirements.txt +ADD keytar.py test_runner.py /app/ +ADD static /app/static + +ENV USER keytar + +ENV PYTHONPATH /env/lib/python2.7/site-packages +ENV CLOUDSDK_PYTHON_SITEPACKAGES $PYTHONPATH + +RUN /bin/bash -c "source ~/.bashrc" + +EXPOSE 8080 +CMD [] +ENTRYPOINT ["/env/bin/python", "keytar.py"] + +ENV PATH /env/bin:$PATH + diff --git a/test/cluster/keytar/README.md b/test/cluster/keytar/README.md new file mode 100644 index 00000000000..081957d192a --- /dev/null +++ b/test/cluster/keytar/README.md @@ -0,0 +1,43 @@ +# Keytar + +Keytar is an internally used Vitess system for continuous execution of cluster tests on Kubernetes/Google Cloud. It monitors docker images on [Docker Hub](https://hub.docker.com). When a new image is uploaded to Docker Hub, Keytar starts a cluster on Google Compute Engine (GKE) and runs Kubernetes applications for the purpose of executing cluster tests. It will then locally run tests against the cluster. It exposes a simple web status page showing test results. + +## Setup + +How to set up Keytar for Vitess: + +* Create service account keys with GKE credentials on the account to run the tests on. Follow [step 1 from the GKE developers page](https://developers.google.com/identity/protocols/application-default-credentials?hl=en_US#howtheywork). +* Move the generated keyfile to `$VTTOP/test/cluster/keytar/config`. +* Create or modify the test configuration file (`$VTTOP/test/cluster/keytar/config/vitess_config.yaml`). +* Ensure the configuration has the correct values for GKE project name and keyfile: + ``` + cluster_setup: + - type: gke + project_name: + keyfile: /config/ + ``` +* Then run the following commands: + ``` + > cd $VTTOP/test/cluster/keytar + > KEYTAR_PASSWORD= KEYTAR_PORT= KEYTAR_CONFIG= ./keytar-up.sh + ``` +* Add a Docker Hub webhook pointing to the Keytar service. The webhook URL should be in the form: + ``` + http://:80/test_request?password= + ``` + +## Dashboard + +The script to start Keytar should output a web address to view the current status. If not, the following command can also be run: +```shell +> kubectl get service keytar -o template --template '{{if ge (len .status.loadBalancer) 1}}{{index (index .status.loadBalancer.ingress 0) "ip"}}{{end}}' +``` + +## Limitations + +Currently, Keytar has the following limitations: + +* Only one configuration file allowed at a time. +* Configuration cannot be updated dynamically. +* Test results are saved in memory and are not durable. +* Results are only shown on the dashboard, there is no notification mechanism. diff --git a/test/cluster/keytar/config/vitess_config.yaml b/test/cluster/keytar/config/vitess_config.yaml new file mode 100644 index 00000000000..e285661a95b --- /dev/null +++ b/test/cluster/keytar/config/vitess_config.yaml @@ -0,0 +1,45 @@ +install: + dependencies: + - python-mysqldb + extra: + - apt-get update + - wget https://storage.googleapis.com/golang/go1.7.4.linux-amd64.tar.gz + - tar -C /usr/local -xzf go1.7.4.linux-amd64.tar.gz + - wget https://storage.googleapis.com/kubernetes-helm/helm-v2.1.3-linux-amd64.tar.gz + - tar -zxvf helm-v2.1.3-linux-amd64.tar.gz + - pip install numpy + - pip install selenium + - pip install --upgrade grpcio==1.0.4 + path: + - /usr/local/go/bin + - /app/linux-amd64/ + cluster_setup: + - type: gke + keyfile: /config/keyfile.json +config: + - docker_image: vitess/root + github: + repo: youtube/vitess + repo_prefix: src/github.com/youtube/vitess + environment: + sandbox: test/cluster/sandbox/vitess_kubernetes_sandbox.py + config: test/cluster/sandbox/example_sandbox.yaml + cluster_type: gke + application_type: k8s + before_test: + - export VTTOP=$(pwd) + - export VTROOT="${VTROOT:-${VTTOP/\/src\/github.com\/youtube\/vitess/}}" + - export GOPATH=$VTROOT + - export PYTHONPATH=$VTTOP/py:$VTTOP/test:$VTTOP/test/cluster/sandbox:/usr/lib/python2.7/dist-packages:/env/lib/python2.7/site-packages + - go get github.com/youtube/vitess/go/cmd/vtctlclient + - export PATH=$GOPATH/bin:$PATH + tests: + - file: test/cluster/drain_test.py + params: + num_drains: 1 + - file: test/cluster/backup_test.py + params: + num_backups: 1 + - file: test/cluster/reparent_test.py + params: + num_reparents: 1 diff --git a/test/cluster/keytar/dummy_test.py b/test/cluster/keytar/dummy_test.py new file mode 100755 index 00000000000..9f779ed4ca4 --- /dev/null +++ b/test/cluster/keytar/dummy_test.py @@ -0,0 +1,18 @@ +#!/usr/bin/env python +"""Dummy no-op test to be used in the webdriver test.""" + +import logging +import sys +import unittest + + +class DummyTest(unittest.TestCase): + + def test_dummy(self): + logging.info('Dummy output.') + + +if __name__ == '__main__': + logging.getLogger().setLevel(logging.INFO) + del sys.argv[1:] + unittest.main() diff --git a/test/cluster/keytar/keytar-controller-template.yaml b/test/cluster/keytar/keytar-controller-template.yaml new file mode 100644 index 00000000000..ccc6f12e1d7 --- /dev/null +++ b/test/cluster/keytar/keytar-controller-template.yaml @@ -0,0 +1,30 @@ +kind: ReplicationController +apiVersion: v1 +metadata: + name: keytar +spec: + replicas: 1 + template: + metadata: + labels: + component: frontend + app: keytar + spec: + containers: + - name: keytar + image: vitess/keytar + ports: + - name: http-server + containerPort: {{port}} + resources: + limits: + memory: "4Gi" + cpu: "500m" + args: ["--config_file", "{{config}}", "--port", "{{port}}", "--password", "{{password}}"] + volumeMounts: + - name: config + mountPath: /config + volumes: + - name: config + configMap: + name: config diff --git a/test/cluster/keytar/keytar-down.sh b/test/cluster/keytar/keytar-down.sh new file mode 100755 index 00000000000..58a02b72297 --- /dev/null +++ b/test/cluster/keytar/keytar-down.sh @@ -0,0 +1,9 @@ +#!/bin/bash + +KUBECTL=${KUBECTL:-kubectl} + +$KUBECTL delete replicationcontroller keytar +$KUBECTL delete service keytar +$KUBECTL delete configmap config +gcloud container clusters delete keytar -z us-central1-b -q +gcloud compute firewall-rules delete keytar -q diff --git a/test/cluster/keytar/keytar-service.yaml b/test/cluster/keytar/keytar-service.yaml new file mode 100644 index 00000000000..097fafdb947 --- /dev/null +++ b/test/cluster/keytar/keytar-service.yaml @@ -0,0 +1,15 @@ +kind: Service +apiVersion: v1 +metadata: + name: keytar + labels: + component: frontend + app: keytar +spec: + ports: + - port: 80 + targetPort: http-server + selector: + component: frontend + app: keytar + type: LoadBalancer diff --git a/test/cluster/keytar/keytar-up.sh b/test/cluster/keytar/keytar-up.sh new file mode 100755 index 00000000000..0c9cb2e2d3e --- /dev/null +++ b/test/cluster/keytar/keytar-up.sh @@ -0,0 +1,39 @@ +#!/bin/bash + +set -e + +KUBECTL=${KUBECTL:-kubectl} + +config_path=${KEYTAR_CONFIG_PATH:-"./config"} +port=${KEYTAR_PORT:-8080} +password=${KEYTAR_PASSWORD:-"defaultkey"} +config=${KEYTAR_CONFIG:-"/config/vitess_config.yaml"} + +sed_script="" +for var in config_path port config password; do + sed_script+="s,{{$var}},${!var},g;" +done + +gcloud container clusters create keytar --machine-type n1-standard-4 --num-nodes 1 --scopes cloud-platform --zone us-central1-b + +echo "Creating keytar configmap" +$KUBECTL create configmap --from-file=$config_path config + +echo "Creating keytar service" +$KUBECTL create -f keytar-service.yaml + +echo "Creating keytar controller" +cat keytar-controller-template.yaml | sed -e "$sed_script" | $KUBECTL create -f - + +echo "Creating firewall-rule" +gcloud compute firewall-rules create keytar --allow tcp:80 + +for i in `seq 1 20`; do + ip=`$KUBECTL get service keytar -o template --template '{{if ge (len .status.loadBalancer) 1}}{{index (index .status.loadBalancer.ingress 0) "ip"}}{{end}}'` + if [[ -n "$ip" ]]; then + echo "Keytar address: http://${ip}:80" + break + fi + echo "Waiting for keytar external IP" + sleep 10 +done diff --git a/test/cluster/keytar/keytar.py b/test/cluster/keytar/keytar.py new file mode 100755 index 00000000000..d1b9d1d01e4 --- /dev/null +++ b/test/cluster/keytar/keytar.py @@ -0,0 +1,299 @@ +#!/usr/bin/env python +"""Keytar flask app. + +This program is responsible for exposing an interface to trigger cluster level +tests. For instance, docker webhooks can be configured to point to this +application in order to trigger tests upon pushing new docker images. +""" + +import argparse +import collections +import datetime +import json +import logging +import os +import Queue +import shutil +import subprocess +import tempfile +import threading +import yaml + +import flask + + +app = flask.Flask(__name__) +results = collections.OrderedDict() +_TEMPLATE = ( + 'python {directory}/test_runner.py -c "{config}" -t {timestamp} ' + '-d {tempdir} -s {server}') + + +class KeytarError(Exception): + pass + + +def run_test_config(config): + """Runs a single test iteration from a configuration.""" + tempdir = tempfile.mkdtemp() + logging.info('Fetching github repository') + + # Get the github repo and clone it. + github_config = config['github'] + github_clone_args, github_repo_dir = _get_download_github_repo_args( + tempdir, github_config) + os.makedirs(github_repo_dir) + subprocess.call(github_clone_args) + + current_dir = os.getcwd() + + timestamp = datetime.datetime.now().strftime('%Y%m%d_%H%M') + results[timestamp] = { + 'timestamp': timestamp, + 'status': 'Start', + 'tests': {}, + 'docker_image': config['docker_image'] + } + + # Generate a test script with the steps described in the configuration, + # as well as the command to execute the test_runner. + with tempfile.NamedTemporaryFile(dir=tempdir, delete=False) as f: + tempscript = f.name + f.write('#!/bin/bash\n') + if 'before_test' in config: + # Change to the github repo directory, any steps to be run before the + # tests should be executed from there. + os.chdir(github_repo_dir) + for before_step in config['before_test']: + f.write('%s\n' % before_step) + server = 'http://localhost:%d' % app.config['port'] + f.write(_TEMPLATE.format( + directory=current_dir, config=yaml.dump(config), timestamp=timestamp, + tempdir=tempdir, server=server)) + os.chmod(tempscript, 0775) + + try: + subprocess.call([tempscript]) + except subprocess.CalledProcessError as e: + logging.warn('Error running test_runner: %s', str(e)) + finally: + os.chdir(current_dir) + shutil.rmtree(tempdir) + + +@app.route('/') +def index(): + return app.send_static_file('index.html') + + +@app.route('/test_results') +def test_results(): + return json.dumps([results[x] for x in sorted(results)]) + + +@app.route('/test_log') +def test_log(): + # Fetch the output from a test. + log = '%s.log' % os.path.basename(flask.request.values['log_name']) + return (flask.send_from_directory('/tmp/testlogs', log), 200, + {'Content-Type': 'text/css'}) + + +@app.route('/update_results', methods=['POST']) +def update_results(): + # Update the results dict, called from the test_runner. + update_args = flask.request.get_json() + timestamp = update_args['timestamp'] + results[timestamp].update(update_args) + return 'OK' + + +def _validate_request(keytar_password, request_values): + """Checks a request against the password provided to the service at startup. + + Raises an exception on errors, otherwise returns None. + + Args: + keytar_password: password provided to the service at startup. + request_values: dict of POST request values provided to Flask. + + Raises: + KeytarError: raised if the password is invalid. + """ + if keytar_password: + if 'password' not in request_values: + raise KeytarError('Expected password not provided in test_request!') + elif request_values['password'] != keytar_password: + raise KeytarError('Incorrect password passed to test_request!') + + +@app.route('/test_request', methods=['POST']) +def test_request(): + """Respond to a post request to execute tests. + + This expects a json payload containing the docker webhook information. + If this app is configured to use a password, the password should be passed in + as part of the POST request. + + Returns: + HTML response. + """ + try: + _validate_request(app.config['password'], flask.request.values) + except KeytarError as e: + flask.abort(400, str(e)) + webhook_data = flask.request.get_json() + repo_name = webhook_data['repository']['repo_name'] + test_configs = [c for c in app.config['keytar_config']['config'] + if c['docker_image'] == repo_name] + if not test_configs: + return 'No config found for repo_name: %s' % repo_name + for test_config in test_configs: + test_worker.add_test(test_config) + return 'OK' + + +def handle_cluster_setup(cluster_setup): + """Setups up a cluster. + + Currently only GKE is supported. This step handles setting up credentials and + ensuring a valid project name is used. + + Args: + cluster_setup: YAML cluster configuration. + + Raises: + KeytarError: raised on invalid setup configurations. + """ + if cluster_setup['type'] != 'gke': + return + + if 'keyfile' not in cluster_setup: + raise KeytarError('No keyfile found in GKE cluster setup!') + # Add authentication steps to allow keytar to start clusters on GKE. + gcloud_args = ['gcloud', 'auth', 'activate-service-account', + '--key-file', cluster_setup['keyfile']] + logging.info('authenticating using keyfile: %s', cluster_setup['keyfile']) + subprocess.call(gcloud_args) + os.environ['GOOGLE_APPLICATION_CREDENTIALS'] = cluster_setup['keyfile'] + + # Ensure that a project name is correctly set. Use the name if provided + # in the configuration, otherwise use the current project name, or else + # the first available project name. + if 'project_name' in cluster_setup: + logging.info('Setting gcloud project to %s', cluster_setup['project_name']) + subprocess.call( + ['gcloud', 'config', 'set', 'project', cluster_setup['project_name']]) + else: + config = subprocess.check_output( + ['gcloud', 'config', 'list', '--format', 'json']) + project_name = json.loads(config)['core']['project'] + if not project_name: + projects = subprocess.check_output(['gcloud', 'projects', 'list']) + first_project = projects[0]['projectId'] + logging.info('gcloud project is unset, setting it to %s', first_project) + subprocess.check_output( + ['gcloud', 'config', 'set', 'project', first_project]) + + +def handle_install_steps(keytar_config): + """Runs all config installation/setup steps. + + Args: + keytar_config: YAML keytar configuration. + """ + if 'install' not in keytar_config: + return + install_config = keytar_config['install'] + for cluster_setup in install_config.get('cluster_setup', []): + handle_cluster_setup(cluster_setup) + + # Install any dependencies using apt-get. + if 'dependencies' in install_config: + subprocess.call(['apt-get', 'update']) + os.environ['DEBIAN_FRONTEND'] = 'noninteractive' + for dep in install_config['dependencies']: + subprocess.call( + ['apt-get', 'install', '-y', '--no-install-recommends', dep]) + + # Run any additional commands if provided. + for step in install_config.get('extra', []): + os.system(step) + + # Update path environment variable. + for path in install_config.get('path', []): + os.environ['PATH'] = '%s:%s' % (path, os.environ['PATH']) + + +def _get_download_github_repo_args(tempdir, github_config): + """Get arguments for github actions. + + Args: + tempdir: Base directory to git clone into. + github_config: Configuration describing the repo, branches, etc. + + Returns: + ([string], string) for arguments to pass to git, and the directory to + clone into. + """ + repo_prefix = github_config.get('repo_prefix', 'github') + repo_dir = os.path.join(tempdir, repo_prefix) + git_args = ['git', 'clone', 'https://github.com/%s' % github_config['repo'], + repo_dir] + if 'branch' in github_config: + git_args += ['-b', github_config['branch']] + return git_args, repo_dir + + +class TestWorker(object): + """A simple test queue. HTTP requests append to this work queue.""" + + def __init__(self): + self.test_queue = Queue.Queue() + self.worker_thread = threading.Thread(target=self.worker_loop) + self.worker_thread.daemon = True + + def worker_loop(self): + # Run forever, executing tests as they are added to the queue. + while True: + item = self.test_queue.get() + run_test_config(item) + self.test_queue.task_done() + + def start(self): + self.worker_thread.start() + + def add_test(self, config): + self.test_queue.put(config) + +test_worker = TestWorker() + + +def main(): + logging.getLogger().setLevel(logging.INFO) + parser = argparse.ArgumentParser(description='Run keytar') + parser.add_argument('--config_file', help='Keytar config file', required=True) + parser.add_argument('--password', help='Password', default=None) + parser.add_argument('--port', help='Port', default=8080, type=int) + keytar_args = parser.parse_args() + with open(keytar_args.config_file, 'r') as yaml_file: + yaml_config = yaml_file.read() + if not yaml_config: + raise ValueError('No valid yaml config!') + keytar_config = yaml.load(yaml_config) + handle_install_steps(keytar_config) + + if not os.path.isdir('/tmp/testlogs'): + os.mkdir('/tmp/testlogs') + + test_worker.start() + + app.config['port'] = keytar_args.port + app.config['password'] = keytar_args.password + app.config['keytar_config'] = keytar_config + + app.run(host='0.0.0.0', port=keytar_args.port, debug=True) + + +if __name__ == '__main__': + main() diff --git a/test/cluster/keytar/keytar_test.py b/test/cluster/keytar/keytar_test.py new file mode 100644 index 00000000000..6f933acfe3a --- /dev/null +++ b/test/cluster/keytar/keytar_test.py @@ -0,0 +1,89 @@ +"""Keytar tests.""" + +import json +import os +import unittest + +import keytar + + +class KeytarTest(unittest.TestCase): + + @classmethod + def setUpClass(cls): + cls.timestamp = '20160101_0000' + if not os.path.isdir('/tmp/testlogs'): + os.mkdir('/tmp/testlogs') + with open( + '/tmp/testlogs/%s_unittest.py.log' % cls.timestamp, 'w') as testlog: + testlog.write('foo') + + def test_validate_request(self): + keytar._validate_request('foo', {'password': 'foo'}) + keytar._validate_request(None, {'password': 'foo'}) + keytar._validate_request(None, {}) + with self.assertRaises(keytar.KeytarError): + keytar._validate_request('foo', {'password': 'foo2'}) + with self.assertRaises(keytar.KeytarError): + keytar._validate_request('foo', {}) + + def test_get_download_github_repo_args(self): + github_config = {'repo': 'youtube/vitess', 'repo_prefix': 'foo'} + + github_clone_args, repo_dir = ( + keytar._get_download_github_repo_args('/tmp', github_config)) + self.assertEquals( + github_clone_args, + ['git', 'clone', 'https://github.com/youtube/vitess', '/tmp/foo']) + self.assertEquals('/tmp/foo', repo_dir) + + github_config = { + 'repo': 'youtube/vitess', 'repo_prefix': 'foo', 'branch': 'bar'} + github_clone_args, repo_dir = ( + keytar._get_download_github_repo_args('/tmp', github_config)) + self.assertEquals( + github_clone_args, + ['git', 'clone', 'https://github.com/youtube/vitess', '/tmp/foo', '-b', + 'bar']) + self.assertEquals('/tmp/foo', repo_dir) + + def test_logs(self): + # Check GET test_results with no results. + tester = keytar.app.test_client(self) + log = tester.get('/test_log?log_name=%s_unittest.py' % self.timestamp) + self.assertEqual(log.status_code, 200) + self.assertEqual(log.data, 'foo') + + def test_results(self): + # Check GET test_results with no results. + tester = keytar.app.test_client(self) + test_results = tester.get('/test_results') + self.assertEqual(test_results.status_code, 200) + self.assertEqual(json.loads(test_results.data), []) + + # Create a test_result, GET test_results should return an entry now. + keytar.results[self.timestamp] = { + 'timestamp': self.timestamp, + 'status': 'Start', + 'tests': {}, + } + test_results = tester.get('/test_results') + self.assertEqual(test_results.status_code, 200) + self.assertEqual( + json.loads(test_results.data), + [{'timestamp': self.timestamp, 'status': 'Start', 'tests': {}}]) + + # Call POST update_results, GET test_results should return a changed entry. + tester.post( + '/update_results', data=json.dumps(dict( + timestamp='20160101_0000', status='Complete')), + follow_redirects=True, content_type='application/json') + test_results = tester.get('/test_results') + self.assertEqual(test_results.status_code, 200) + self.assertEqual( + json.loads(test_results.data), + [{'timestamp': self.timestamp, 'status': 'Complete', 'tests': {}}]) + + +if __name__ == '__main__': + unittest.main() diff --git a/test/cluster/keytar/keytar_web_test.py b/test/cluster/keytar/keytar_web_test.py new file mode 100755 index 00000000000..5c16569e2cb --- /dev/null +++ b/test/cluster/keytar/keytar_web_test.py @@ -0,0 +1,59 @@ +#!/usr/bin/env python +"""A keytar webdriver test.""" + +import json +import logging +import signal +import subprocess +import time +import os +from selenium import webdriver +import unittest +import urllib2 + +import environment + + +class TestKeytarWeb(unittest.TestCase): + + @classmethod + def setUpClass(cls): + cls.driver = environment.create_webdriver() + port = environment.reserve_ports(1) + keytar_folder = os.path.join(environment.vttop, 'test/cluster/keytar') + cls.flask_process = subprocess.Popen( + [os.path.join(keytar_folder, 'keytar.py'), + '--config_file=%s' % os.path.join(keytar_folder, 'test_config.yaml'), + '--port=%d' % port, '--password=foo'], + preexec_fn=os.setsid) + cls.flask_addr = 'http://localhost:%d' % port + + @classmethod + def tearDownClass(cls): + os.killpg(cls.flask_process.pid, signal.SIGTERM) + cls.driver.quit() + + def _wait_for_complete_status(self, timeout_s=180): + start_time = time.time() + while time.time() - start_time < timeout_s: + if 'Complete' in self.driver.find_element_by_id('results').text: + return + self.driver.refresh() + time.sleep(5) + self.fail('Timed out waiting for test to finish.') + + def test_keytar_web(self): + self.driver.get(self.flask_addr) + req = urllib2.Request('%s/test_request?password=foo' % self.flask_addr) + req.add_header('Content-Type', 'application/json') + urllib2.urlopen( + req, json.dumps({'repository': {'repo_name': 'test/image'}})) + self._wait_for_complete_status() + logging.info('Dummy test complete.') + self.driver.find_element_by_partial_link_text('PASSED').click() + self.assertIn('Dummy output.', + self.driver.find_element_by_tag_name('body').text) + + +if __name__ == '__main__': + unittest.main() diff --git a/test/cluster/keytar/requirements.txt b/test/cluster/keytar/requirements.txt new file mode 100644 index 00000000000..b02181264fe --- /dev/null +++ b/test/cluster/keytar/requirements.txt @@ -0,0 +1,2 @@ +Flask==0.10 +pyyaml==3.10 diff --git a/test/cluster/keytar/static/index.html b/test/cluster/keytar/static/index.html new file mode 100644 index 00000000000..153b752ae55 --- /dev/null +++ b/test/cluster/keytar/static/index.html @@ -0,0 +1,22 @@ + + + + + + + + Keytar + + + + +
+

Waiting for test results...

+
+ + + + + diff --git a/test/cluster/keytar/static/script.js b/test/cluster/keytar/static/script.js new file mode 100644 index 00000000000..29275ffd418 --- /dev/null +++ b/test/cluster/keytar/static/script.js @@ -0,0 +1,42 @@ +$(document).ready(function() { + var resultsElement = $("#test-results"); + + var appendTestResults = function(data) { + resultsElement.empty(); + var html = " \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + "; + $.each(data, function(key, value) { + html += ""; + }); + html += "
TimeDocker ImageSandbox NameStatusTestsResults
" + value.timestamp + "" + value.docker_image + "" + value.name + "" + value.status + ""; + $.each(value.tests, function(key, val) { + html += ""; + }); + html += "
" + key + "
"; + $.each(value.tests, function(key, val) { + html += ""; + }); + html += "
" + val + "
"; + resultsElement.append(html); + }; + + // Poll every second. + var fetchTestResults = function() { + $.getJSON("/test_results").done(appendTestResults).always( + function() { + setTimeout(fetchTestResults, 60000); + }); + }; + fetchTestResults(); +}); diff --git a/test/cluster/keytar/static/style.css b/test/cluster/keytar/static/style.css new file mode 100644 index 00000000000..fd1c393fb08 --- /dev/null +++ b/test/cluster/keytar/static/style.css @@ -0,0 +1,61 @@ +body, input { + color: #123; + font-family: "Gill Sans", sans-serif; +} + +div { + overflow: hidden; + padding: 1em 0; + position: relative; + text-align: center; +} + +h1, h2, p, input, a { + font-weight: 300; + margin: 0; +} + +h1 { + color: #BDB76B; + font-size: 3.5em; +} + +h2 { + color: #999; +} + +form { + margin: 0 auto; + max-width: 50em; + text-align: center; +} + +input { + border: 0; + border-radius: 1000px; + box-shadow: inset 0 0 0 2px #BDB76B; + display: inline; + font-size: 1.5em; + margin-bottom: 1em; + outline: none; + padding: .5em 5%; + width: 55%; +} + +form a { + background: #BDB76B; + border: 0; + border-radius: 1000px; + color: #FFF; + font-size: 1.25em; + font-weight: 400; + padding: .75em 2em; + text-decoration: none; + text-transform: uppercase; + white-space: normal; +} + +p { + font-size: 1.5em; + line-height: 1.5; +} diff --git a/test/cluster/keytar/test_config.yaml b/test/cluster/keytar/test_config.yaml new file mode 100644 index 00000000000..a2f92a572d4 --- /dev/null +++ b/test/cluster/keytar/test_config.yaml @@ -0,0 +1,15 @@ +install: + path: + - /test_path +config: + - docker_image: test/image + github: + repo: youtube/vitess + repo_prefix: src/github.com/youtube/vitess + before_test: + - touch /tmp/test_file + environment: + cluster_type: gke + application_type: k8s + tests: + - file: test/cluster/keytar/dummy_test.py diff --git a/test/cluster/keytar/test_runner.py b/test/cluster/keytar/test_runner.py new file mode 100755 index 00000000000..b23d991d48b --- /dev/null +++ b/test/cluster/keytar/test_runner.py @@ -0,0 +1,115 @@ +"""Script to run a single cluster test. + +This includes the following steps: + 1. Starting a test cluster (GKE supported). + 2. Running tests against the cluster. + 3. Reporting test results. +""" + +import argparse +import json +import logging +import os +import subprocess +import urllib2 +import uuid +import yaml + +keytar_args = None + + +def update_result(k, v): + """Post a key/value pair test result update.""" + url = '%s/update_results' % keytar_args.server + req = urllib2.Request(url) + req.add_header('Content-Type', 'application/json') + urllib2.urlopen(req, json.dumps({k: v, 'timestamp': keytar_args.timestamp})) + + +def run_sandbox_action(environment_config, name, action): + """Run a sandbox action (Start/Stop). + + Args: + environment_config: yaml configuration for the sandbox. + name: unique name for the sandbox. + action: action to pass to the sandbox action parameter. + """ + if 'sandbox' not in environment_config: + return + # Execute sandbox command + sandbox_file = os.path.join(repo_dir, environment_config['sandbox']) + os.chdir(os.path.dirname(sandbox_file)) + sandbox_args = [ + './%s' % os.path.basename(sandbox_file), + '-e', environment_config['cluster_type'], '-n', name, '-k', name, + '-c', os.path.join(repo_dir, environment_config['config']), + '-a', action] + update_result('status', 'Running sandbox action: %s' % action) + try: + subprocess.check_call(sandbox_args) + update_result('status', 'Finished sandbox action: %s' % action) + except subprocess.CalledProcessError as e: + logging.info('Failed to run sandbox action %s: %s', (action, e.output)) + update_result('status', 'Sandbox failure') + + +def run_test_config(): + """Runs a single test iteration from a configuration. + + This includes bringing up an environment, running the tests, and reporting + status. + """ + # Generate a random name. Kubernetes/GKE has name length limits. + name = 'keytar%s' % format(uuid.uuid4().fields[0], 'x') + update_result('name', name) + + environment_config = config['environment'] + run_sandbox_action(environment_config, name, 'Start') + logging.info('Running tests') + update_result('status', 'Running Tests') + + try: + # Run tests and update results. + test_results = {} + for test in config['tests']: + test_file = os.path.join(repo_dir, test['file']) + test_name = os.path.basename(test_file) + logging.info('Running test %s', test_name) + os.chdir(os.path.dirname(test_file)) + test_args = [ + './%s' % test_name, + '-e', environment_config['application_type'], '-n', name] + if 'params' in test: + test_args += ['-t', ':'.join( + '%s=%s' % (k, v) for (k, v) in test['params'].iteritems())] + testlog = '/tmp/testlogs/%s_%s.log' % (keytar_args.timestamp, test_name) + logging.info('Saving log to %s', testlog) + test_results[test_name] = 'RUNNING' + update_result('tests', test_results) + with open(testlog, 'w') as results_file: + if subprocess.call(test_args, stdout=results_file, stderr=results_file): + test_results[test_name] = 'FAILED' + else: + test_results[test_name] = 'PASSED' + update_result('tests', test_results) + update_result('status', 'Tests Complete') + except Exception as e: # pylint: disable=broad-except + logging.info('Exception caught: %s', str(e)) + update_result('status', 'System Error running tests: %s' % str(e)) + finally: + run_sandbox_action(environment_config, name, 'Stop') + + +if __name__ == '__main__': + logging.getLogger().setLevel(logging.INFO) + parser = argparse.ArgumentParser(description='Run keytar') + parser.add_argument('-c', '--config', help='Keytar config yaml') + parser.add_argument('-t', '--timestamp', help='Timestamp string') + parser.add_argument('-d', '--dir', help='temp dir created for the test') + parser.add_argument('-s', '--server', help='keytar server address') + keytar_args = parser.parse_args() + config = yaml.load(keytar_args.config) + repo_prefix = config['github'].get('repo_prefix', 'github') + repo_dir = os.path.join(keytar_args.dir, repo_prefix) + + run_test_config() diff --git a/test/config.json b/test/config.json index 03cffaa5e59..0996dd59c96 100644 --- a/test/config.json +++ b/test/config.json @@ -280,6 +280,17 @@ "worker_test" ] }, + "resharding_rbr": { + "File": "resharding_rbr.py", + "Args": [], + "Command": [], + "Manual": false, + "Shard": 0, + "RetryMax": 0, + "Tags": [ + "worker_test" + ] + }, "schema": { "File": "schema.py", "Args": [], @@ -385,6 +396,15 @@ "RetryMax": 0, "Tags": [] }, + "update_stream_rbr": { + "File": "update_stream_rbr.py", + "Args": [], + "Command": [], + "Manual": false, + "Shard": 4, + "RetryMax": 0, + "Tags": [] + }, "vertical_split": { "File": "vertical_split.py", "Args": [], diff --git a/test/resharding.py b/test/resharding.py index 7fa8c923377..ce8b738dd17 100755 --- a/test/resharding.py +++ b/test/resharding.py @@ -70,7 +70,8 @@ def setUpModule(): try: environment.topo_server().setup() - setup_procs = [t.init_mysql() for t in all_tablets] + setup_procs = [t.init_mysql(use_rbr=base_sharding.use_rbr) + for t in all_tablets] utils.Vtctld().start() utils.wait_procs(setup_procs) except: @@ -728,8 +729,17 @@ def test_resharding(self): utils.pause('Good time to test vtworker for diffs') # get status for destination master tablets, make sure we have it all - self.check_running_binlog_player(shard_2_master, 4022, 2008) - self.check_running_binlog_player(shard_3_master, 4024, 2008) + if base_sharding.use_rbr: + # We submitted non-annotated DMLs, that are properly routed + # with RBR, but not with SBR. So the first shard counts + # are smaller. In the second shard, we submitted statements + # that affect more than one keyspace id. These will result + # in two queries with RBR. So the count there is higher. + self.check_running_binlog_player(shard_2_master, 4018, 2008) + self.check_running_binlog_player(shard_3_master, 4028, 2008) + else: + self.check_running_binlog_player(shard_2_master, 4022, 2008) + self.check_running_binlog_player(shard_3_master, 4024, 2008) # start a thread to insert data into shard_1 in the background # with current time, and monitor the delay diff --git a/test/resharding_rbr.py b/test/resharding_rbr.py new file mode 100755 index 00000000000..f70e3100f3c --- /dev/null +++ b/test/resharding_rbr.py @@ -0,0 +1,15 @@ +#!/usr/bin/env python +# +# Copyright 2017, Google Inc. All rights reserved. +# Use of this source code is governed by a BSD-style license that can +# be found in the LICENSE file. + +"""Re-runs resharding.py with RBR.""" + +import base_sharding +import resharding +import utils + +if __name__ == '__main__': + base_sharding.use_rbr = True + utils.main(resharding) diff --git a/test/tablet.py b/test/tablet.py index fa7df312d21..c57c5bb6bb5 100644 --- a/test/tablet.py +++ b/test/tablet.py @@ -180,7 +180,8 @@ def mysqlctld(self, cmd, extra_my_cnf=None, verbose=False, extra_args=None): args.extend(cmd) return utils.run_bg(args, extra_env=extra_env) - def init_mysql(self, extra_my_cnf=None, init_db=None, extra_args=None): + def init_mysql(self, extra_my_cnf=None, init_db=None, extra_args=None, + use_rbr=False): """Init the mysql tablet directory, starts mysqld. Either runs 'mysqlctl init', or starts a mysqlctld process. @@ -189,10 +190,17 @@ def init_mysql(self, extra_my_cnf=None, init_db=None, extra_args=None): extra_my_cnf: to pass to mysqlctl. init_db: if set, use this init_db script instead of the default. extra_args: passed to mysqlctld / mysqlctl. + use_rbr: configure the MySQL daemon to use RBR. Returns: The forked process. """ + if use_rbr: + if extra_my_cnf: + extra_my_cnf += ':' + environment.vttop + '/config/mycnf/rbr.cnf' + else: + extra_my_cnf = environment.vttop + '/config/mycnf/rbr.cnf' + if not init_db: init_db = environment.vttop + '/config/init_db.sql' diff --git a/test/update_stream.py b/test/update_stream.py index e78f010d7a6..2a01a4563c4 100755 --- a/test/update_stream.py +++ b/test/update_stream.py @@ -16,6 +16,9 @@ from protocols_flavor import protocols_flavor from vtgate_gateway_flavor.gateway import vtgate_gateway_flavor +# global flag to control which type of replication we use. +use_rbr = False + master_tablet = tablet.Tablet() replica_tablet = tablet.Tablet() @@ -59,8 +62,8 @@ def setUpModule(): environment.topo_server().setup() # start mysql instance external to the test - setup_procs = [master_tablet.init_mysql(), - replica_tablet.init_mysql()] + setup_procs = [master_tablet.init_mysql(use_rbr=use_rbr), + replica_tablet.init_mysql(use_rbr=use_rbr)] utils.wait_procs(setup_procs) # start a vtctld so the vtctl insert commands are just RPCs, not forks diff --git a/test/update_stream_rbr.py b/test/update_stream_rbr.py new file mode 100755 index 00000000000..6625b763dc3 --- /dev/null +++ b/test/update_stream_rbr.py @@ -0,0 +1,14 @@ +#!/usr/bin/env python +# +# Copyright 2017, Google Inc. All rights reserved. +# Use of this source code is governed by a BSD-style license that can +# be found in the LICENSE file. + +"""Re-runs update_stream.py with RBR.""" + +import update_stream +import utils + +if __name__ == '__main__': + update_stream.use_rbr = True + utils.main(update_stream) diff --git a/test/vertical_split.py b/test/vertical_split.py index 72277debaa4..64205f5c510 100755 --- a/test/vertical_split.py +++ b/test/vertical_split.py @@ -18,9 +18,6 @@ from vtdb import keyrange_constants from vtdb import vtgate_client -# Global variables, for tests flavors. -use_rbr = False - # source keyspace, with 4 tables source_master = tablet.Tablet() source_replica = tablet.Tablet() @@ -41,10 +38,8 @@ def setUpModule(): try: environment.topo_server().setup() - extra_my_cnf = None - if use_rbr: - extra_my_cnf = environment.vttop + '/config/mycnf/rbr.cnf' - setup_procs = [t.init_mysql(extra_my_cnf=extra_my_cnf) for t in all_tablets] + setup_procs = [t.init_mysql(use_rbr=base_sharding.use_rbr) + for t in all_tablets] utils.Vtctld().start() utils.wait_procs(setup_procs) except: diff --git a/test/vertical_split_rbr.py b/test/vertical_split_rbr.py index 9b36fe74e77..39c1e6c6ddc 100755 --- a/test/vertical_split_rbr.py +++ b/test/vertical_split_rbr.py @@ -6,9 +6,10 @@ """Re-runs resharding.py with RBR on.""" +import base_sharding import vertical_split import utils if __name__ == '__main__': - vertical_split.use_rbr = True + base_sharding.use_rbr = True utils.main(vertical_split) diff --git a/web/vtctld2/app/674f50d287a8c48dc19ba404d20fe713.eot b/web/vtctld2/app/674f50d287a8c48dc19ba404d20fe713.eot new file mode 100644 index 00000000000..e9f60ca953f Binary files /dev/null and b/web/vtctld2/app/674f50d287a8c48dc19ba404d20fe713.eot differ diff --git a/web/vtctld2/app/912ec66d7572ff821749319396470bde.svg b/web/vtctld2/app/912ec66d7572ff821749319396470bde.svg new file mode 100644 index 00000000000..855c845e538 --- /dev/null +++ b/web/vtctld2/app/912ec66d7572ff821749319396470bde.svg @@ -0,0 +1,2671 @@ + + + + +Created by FontForge 20120731 at Mon Oct 24 17:37:40 2016 + By ,,, +Copyright Dave Gandy 2016. All rights reserved. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/web/vtctld2/app/af7ae505a9eed503f8b8e6982036873e.woff2 b/web/vtctld2/app/af7ae505a9eed503f8b8e6982036873e.woff2 new file mode 100644 index 00000000000..4d13fc60404 Binary files /dev/null and b/web/vtctld2/app/af7ae505a9eed503f8b8e6982036873e.woff2 differ diff --git a/web/vtctld2/app/b06871f281fee6b241d60582ae9369b9.ttf b/web/vtctld2/app/b06871f281fee6b241d60582ae9369b9.ttf new file mode 100644 index 00000000000..35acda2fa11 Binary files /dev/null and b/web/vtctld2/app/b06871f281fee6b241d60582ae9369b9.ttf differ diff --git a/web/vtctld2/app/fee66e712a8a08eef5805a46892932ad.woff b/web/vtctld2/app/fee66e712a8a08eef5805a46892932ad.woff new file mode 100644 index 00000000000..400014a4b06 Binary files /dev/null and b/web/vtctld2/app/fee66e712a8a08eef5805a46892932ad.woff differ diff --git a/web/vtctld2/app/index.html b/web/vtctld2/app/index.html index a44fadba808..89623866f63 100644 --- a/web/vtctld2/app/index.html +++ b/web/vtctld2/app/index.html @@ -27,5 +27,5 @@ Loading... - + diff --git a/web/vtctld2/app/inline.js b/web/vtctld2/app/inline.js index c4742f8276d..5e5d587932d 100644 --- a/web/vtctld2/app/inline.js +++ b/web/vtctld2/app/inline.js @@ -1 +1 @@ -!function(e){function __webpack_require__(r){if(t[r])return t[r].exports;var n=t[r]={i:r,l:!1,exports:{}};return e[r].call(n.exports,n,n.exports,__webpack_require__),n.l=!0,n.exports}var r=window.webpackJsonp;window.webpackJsonp=function(t,o,c){for(var _,a,i,u=0,p=[];u1;){var o=r.shift();i=i.hasOwnProperty(o)&&isPresent(i[o])?i[o]:i[o]={}}void 0!==i&&null!==i||(i={}),i[r.shift()]=n}function getSymbolIterator(){if(isBlank(h))if(isPresent(n.Symbol)&&isPresent(Symbol.iterator))h=Symbol.iterator;else for(var e=Object.getOwnPropertyNames(Map.prototype),t=0;t=0&&e[r]==t;r--)n--;e=e.substring(0,n)}return e},StringWrapper.replace=function(e,t,n){return e.replace(t,n)},StringWrapper.replaceAll=function(e,t,n){return e.replace(t,n)},StringWrapper.slice=function(e,t,n){return void 0===t&&(t=0),void 0===n&&(n=null),e.slice(t,null===n?void 0:n)},StringWrapper.replaceAllMapped=function(e,t,n){return e.replace(t,function(){for(var e=[],t=0;tt?1:0},StringWrapper}();t.StringWrapper=s;var a=function(){function StringJoiner(e){void 0===e&&(e=[]),this.parts=e}return StringJoiner.prototype.add=function(e){this.parts.push(e)},StringJoiner.prototype.toString=function(){return this.parts.join("")},StringJoiner}();t.StringJoiner=a;var l=function(e){function NumberParseError(t){e.call(this),this.message=t}return r(NumberParseError,e),NumberParseError.prototype.toString=function(){return this.message},NumberParseError}(Error);t.NumberParseError=l;var c=function(){function NumberWrapper(){}return NumberWrapper.toFixed=function(e,t){return e.toFixed(t)},NumberWrapper.equal=function(e,t){return e===t},NumberWrapper.parseIntAutoRadix=function(e){var t=parseInt(e);if(isNaN(t))throw new l("Invalid integer literal when parsing "+e);return t},NumberWrapper.parseInt=function(e,t){if(10==t){if(/^(\-|\+)?[0-9]+$/.test(e))return parseInt(e,t)}else if(16==t){if(/^(\-|\+)?[0-9ABCDEFabcdef]+$/.test(e))return parseInt(e,t)}else{var n=parseInt(e,t);if(!isNaN(n))return n}throw new l("Invalid integer literal when parsing "+e+" in base "+t)},NumberWrapper.parseFloat=function(e){return parseFloat(e)},Object.defineProperty(NumberWrapper,"NaN",{get:function(){return NaN},enumerable:!0,configurable:!0}),NumberWrapper.isNumeric=function(e){return!isNaN(e-parseFloat(e))},NumberWrapper.isNaN=function(e){return isNaN(e)},NumberWrapper.isInteger=function(e){return Number.isInteger(e)},NumberWrapper}();t.NumberWrapper=c,t.RegExp=i.RegExp;var u=function(){function FunctionWrapper(){}return FunctionWrapper.apply=function(e,t){return e.apply(null,t)},FunctionWrapper.bind=function(e,t){return e.bind(t)},FunctionWrapper}();t.FunctionWrapper=u,t.looseIdentical=looseIdentical,t.getMapKey=getMapKey,t.normalizeBlank=normalizeBlank,t.normalizeBool=normalizeBool,t.isJsObject=isJsObject,t.print=print,t.warn=warn;var p=function(){function Json(){}return Json.parse=function(e){return i.JSON.parse(e)},Json.stringify=function(e){return i.JSON.stringify(e,null,2)},Json}();t.Json=p;var d=function(){function DateWrapper(){}return DateWrapper.create=function(e,n,r,i,o,s,a){return void 0===n&&(n=1),void 0===r&&(r=1),void 0===i&&(i=0),void 0===o&&(o=0),void 0===s&&(s=0),void 0===a&&(a=0),new t.Date(e,n-1,r,i,o,s,a)},DateWrapper.fromISOString=function(e){return new t.Date(e)},DateWrapper.fromMillis=function(e){return new t.Date(e)},DateWrapper.toMillis=function(e){return e.getTime()},DateWrapper.now=function(){return new t.Date},DateWrapper.toJson=function(e){return e.toJSON()},DateWrapper}();t.DateWrapper=d,t.setValueOnPath=setValueOnPath;var h=null;t.getSymbolIterator=getSymbolIterator,t.evalExpression=evalExpression,t.isPrimitive=isPrimitive,t.hasConstructor=hasConstructor,t.escape=escape,t.escapeRegExp=escapeRegExp}).call(t,n(70))},4,function(e,t,n){"use strict";var r=this&&this.__extends||function(e,t){function __(){this.constructor=e}for(var n in t)t.hasOwnProperty(n)&&(e[n]=t[n]);e.prototype=null===t?Object.create(t):(__.prototype=t.prototype,new __)},i=n(214),o=n(42),s=n(212),a=n(898),l=function(e){function Subscriber(t,n,r){switch(e.call(this),this.syncErrorValue=null,this.syncErrorThrown=!1,this.syncErrorThrowable=!1,this.isStopped=!1,arguments.length){case 0:this.destination=a.empty;break;case 1:if(!t){this.destination=a.empty;break}if("object"==typeof t){t instanceof Subscriber?(this.destination=t,this.destination.add(this)):(this.syncErrorThrowable=!0,this.destination=new c(this,t));break}default:this.syncErrorThrowable=!0,this.destination=new c(this,t,n,r)}}return r(Subscriber,e),Subscriber.create=function(e,t,n){var r=new Subscriber(e,t,n);return r.syncErrorThrowable=!1,r},Subscriber.prototype.next=function(e){this.isStopped||this._next(e)},Subscriber.prototype.error=function(e){this.isStopped||(this.isStopped=!0,this._error(e))},Subscriber.prototype.complete=function(){this.isStopped||(this.isStopped=!0,this._complete())},Subscriber.prototype.unsubscribe=function(){this.isUnsubscribed||(this.isStopped=!0,e.prototype.unsubscribe.call(this))},Subscriber.prototype._next=function(e){this.destination.next(e)},Subscriber.prototype._error=function(e){this.destination.error(e),this.unsubscribe()},Subscriber.prototype._complete=function(){this.destination.complete(),this.unsubscribe()},Subscriber.prototype[s.$$rxSubscriber]=function(){return this},Subscriber}(o.Subscription);t.Subscriber=l;var c=function(e){function SafeSubscriber(t,n,r,o){e.call(this),this._parent=t;var s,a=this;i.isFunction(n)?s=n:n&&(a=n,s=n.next,r=n.error,o=n.complete,i.isFunction(a.unsubscribe)&&this.add(a.unsubscribe.bind(a)),a.unsubscribe=this.unsubscribe.bind(this)),this._context=a,this._next=s,this._error=r,this._complete=o}return r(SafeSubscriber,e),SafeSubscriber.prototype.next=function(e){if(!this.isStopped&&this._next){var t=this._parent;t.syncErrorThrowable?this.__tryOrSetError(t,this._next,e)&&this.unsubscribe():this.__tryOrUnsub(this._next,e)}},SafeSubscriber.prototype.error=function(e){if(!this.isStopped){var t=this._parent;if(this._error)t.syncErrorThrowable?(this.__tryOrSetError(t,this._error,e),this.unsubscribe()):(this.__tryOrUnsub(this._error,e),this.unsubscribe());else{if(!t.syncErrorThrowable)throw this.unsubscribe(),e;t.syncErrorValue=e,t.syncErrorThrown=!0,this.unsubscribe()}}},SafeSubscriber.prototype.complete=function(){if(!this.isStopped){var e=this._parent;this._complete?e.syncErrorThrowable?(this.__tryOrSetError(e,this._complete),this.unsubscribe()):(this.__tryOrUnsub(this._complete),this.unsubscribe()):this.unsubscribe()}},SafeSubscriber.prototype.__tryOrUnsub=function(e,t){try{e.call(this._context,t)}catch(n){throw this.unsubscribe(),n}},SafeSubscriber.prototype.__tryOrSetError=function(e,t,n){try{t.call(this._context,n)}catch(r){return e.syncErrorValue=r,e.syncErrorThrown=!0,!0}return!1},SafeSubscriber.prototype._unsubscribe=function(){var e=this._parent;this._context=null,this._parent=null,e.unsubscribe()},SafeSubscriber}(l)},4,function(e,t,n){var r=n(17);e.exports=function(e){if(!r(e))throw TypeError(e+" is not an object!");return e}},function(e,t,n){"use strict";var r=this&&this.__decorate||function(e,t,n,r){var i,o=arguments.length,s=o<3?t:null===r?r=Object.getOwnPropertyDescriptor(t,n):r;if("object"==typeof Reflect&&"function"==typeof Reflect.decorate)s=Reflect.decorate(e,t,n,r);else for(var a=e.length-1;a>=0;a--)(i=e[a])&&(s=(o<3?i(s):o>3?i(t,n,s):i(t,n))||s);return o>3&&s&&Object.defineProperty(t,n,s),s},i=this&&this.__metadata||function(e,t){if("object"==typeof Reflect&&"function"==typeof Reflect.metadata)return Reflect.metadata(e,t)},o=n(0),s=function(){function DomHandler(){}return DomHandler.prototype.addClass=function(e,t){e.classList?e.classList.add(t):e.className+=" "+t},DomHandler.prototype.addMultipleClasses=function(e,t){if(e.classList)for(var n=t.split(" "),r=0;rwindow.innerHeight?-1*i.height:o,r=a.left+i.width>window.innerWidth?s-i.width:0,e.style.top=n+"px",e.style.left=r+"px"},DomHandler.prototype.absolutePosition=function(e,t){var n,r,i=e.offsetParent?{width:e.offsetWidth,height:e.offsetHeight}:this.getHiddenElementDimensions(e),o=i.height,s=i.width,a=t.offsetHeight,l=t.offsetWidth,c=t.getBoundingClientRect(),u=this.getWindowScrollTop(),p=this.getWindowScrollLeft();n=c.top+a+o>window.innerHeight?c.top+u-o:a+c.top+u,r=c.left+l+s>window.innerWidth?c.left+p+l-s:c.left+p,e.style.top=n+"px",e.style.left=r+"px"},DomHandler.prototype.getHiddenElementOuterHeight=function(e){e.style.visibility="hidden",e.style.display="block";var t=e.offsetHeight;return e.style.display="none",e.style.visibility="visible",t},DomHandler.prototype.getHiddenElementOuterWidth=function(e){e.style.visibility="hidden",e.style.display="block";var t=e.offsetWidth;return e.style.display="none",e.style.visibility="visible",t},DomHandler.prototype.getHiddenElementDimensions=function(e){var t={};return e.style.visibility="hidden",e.style.display="block",t.width=e.offsetWidth,t.height=e.offsetHeight,e.style.display="none",e.style.visibility="visible",t},DomHandler.prototype.scrollInView=function(e,t){var n=getComputedStyle(e).getPropertyValue("borderTopWidth"),r=n?parseFloat(n):0,i=getComputedStyle(e).getPropertyValue("paddingTop"),o=i?parseFloat(i):0,s=e.getBoundingClientRect(),a=t.getBoundingClientRect(),l=a.top+document.body.scrollTop-(s.top+document.body.scrollTop)-r-o,c=e.scrollTop,u=e.clientHeight,p=this.getOuterHeight(t);l<0?e.scrollTop=c+l:l+p>u&&(e.scrollTop=c+l-u+p)},DomHandler.prototype.fadeIn=function(e,t){e.style.opacity=0;var n=+new Date,r=function(){e.style.opacity=+e.style.opacity+((new Date).getTime()-n)/t,n=+new Date,+e.style.opacity<1&&(window.requestAnimationFrame&&requestAnimationFrame(r)||setTimeout(r,16))};r()},DomHandler.prototype.fadeOut=function(e,t){var n=1,r=50,i=t,o=r/i,s=setInterval(function(){n-=o,e.style.opacity=n,n<=0&&clearInterval(s)},r)},DomHandler.prototype.getWindowScrollTop=function(){var e=document.documentElement;return(window.pageYOffset||e.scrollTop)-(e.clientTop||0)},DomHandler.prototype.getWindowScrollLeft=function(){var e=document.documentElement;return(window.pageXOffset||e.scrollLeft)-(e.clientLeft||0)},DomHandler.prototype.matches=function(e,t){var n=Element.prototype,r=n.matches||n.webkitMatchesSelector||n.mozMatchesSelector||n.msMatchesSelector||function(e){return[].indexOf.call(document.querySelectorAll(e),this)!==-1};return r.call(e,t)},DomHandler.prototype.getOuterWidth=function(e,t){var n=e.offsetWidth;if(t){var r=getComputedStyle(e);n+=parseInt(r.paddingLeft)+parseInt(r.paddingRight)}return n},DomHandler.prototype.getHorizontalMargin=function(e){var t=getComputedStyle(e);return parseInt(t.marginLeft)+parseInt(t.marginRight)},DomHandler.prototype.innerWidth=function(e){var t=e.offsetWidth,n=getComputedStyle(e);return t+=parseInt(n.paddingLeft)+parseInt(n.paddingRight)},DomHandler.prototype.width=function(e){var t=e.offsetWidth,n=getComputedStyle(e);return t-=parseInt(n.paddingLeft)+parseInt(n.paddingRight)},DomHandler.prototype.getOuterHeight=function(e,t){var n=e.offsetHeight;if(t){var r=getComputedStyle(e);n+=parseInt(r.marginTop)+parseInt(r.marginBottom)}return n},DomHandler.prototype.getHeight=function(e){var t=e.offsetHeight,n=getComputedStyle(e);return t-=parseInt(n.paddingTop)+parseInt(n.paddingBottom)+parseInt(n.borderTopWidth)+parseInt(n.borderBottomWidth)},DomHandler.prototype.getViewport=function(){var e=window,t=document,n=t.documentElement,r=t.getElementsByTagName("body")[0],i=e.innerWidth||n.clientWidth||r.clientWidth,o=e.innerHeight||n.clientHeight||r.clientHeight;return{width:i,height:o}},DomHandler.prototype.equals=function(e,t){if(null==e||null==t)return!1;if(e==t)return!0;if("object"==typeof e&&"object"==typeof t){for(var n in e){if(e.hasOwnProperty(n)!==t.hasOwnProperty(n))return!1;switch(typeof e[n]){case"object":if(!this.equals(e[n],t[n]))return!1;break;case"function":if("undefined"==typeof t[n]||"compare"!=n&&e[n].toString()!=t[n].toString())return!1;break;default:if(e[n]!=t[n])return!1}}for(var n in t)if("undefined"==typeof e[n])return!1;return!0}return!1},DomHandler.zindex=1e3,DomHandler=r([o.Injectable(),i("design:paramtypes",[])],DomHandler)}();t.DomHandler=s},[1094,5],function(e,t,n){"use strict";var r=this&&this.__extends||function(e,t){function __(){this.constructor=e}for(var n in t)t.hasOwnProperty(n)&&(e[n]=t[n]);e.prototype=null===t?Object.create(t):(__.prototype=t.prototype,new __)},i=n(6),o=function(e){function OuterSubscriber(){e.apply(this,arguments)}return r(OuterSubscriber,e),OuterSubscriber.prototype.notifyNext=function(e,t,n,r,i){this.destination.next(t)},OuterSubscriber.prototype.notifyError=function(e,t){this.destination.error(e)},OuterSubscriber.prototype.notifyComplete=function(e){this.destination.complete()},OuterSubscriber}(i.Subscriber);t.OuterSubscriber=o},function(e,t,n){"use strict";function subscribeToResult(e,t,n,u){var p=new c.InnerSubscriber(e,n,u);if(!p.isUnsubscribed){if(t instanceof s.Observable)return t._isScalar?(p.next(t.value),void p.complete()):t.subscribe(p);if(i.isArray(t)){for(var d=0,h=t.length;d=0;a--)(i=e[a])&&(s=(o<3?i(s):o>3?i(t,n,s):i(t,n))||s);return o>3&&s&&Object.defineProperty(t,n,s),s},i=this&&this.__metadata||function(e,t){if("object"==typeof Reflect&&"function"==typeof Reflect.metadata)return Reflect.metadata(e,t)},o=n(0),s=n(3),a=n(0),l=function(){function Header(){}return Header=r([a.Component({selector:"header",template:""}),i("design:paramtypes",[])],Header)}();t.Header=l;var c=function(){function Footer(){}return Footer=r([a.Component({selector:"footer",template:""}),i("design:paramtypes",[])],Footer)}();t.Footer=c;var u=function(){function TemplateWrapper(e){this.viewContainer=e}return TemplateWrapper.prototype.ngOnInit=function(){this.viewContainer.createEmbeddedView(this.templateRef,{$implicit:this.item})},r([o.Input(),i("design:type",Object)],TemplateWrapper.prototype,"item",void 0),r([o.Input("pTemplateWrapper"),i("design:type",o.TemplateRef)],TemplateWrapper.prototype,"templateRef",void 0),TemplateWrapper=r([o.Directive({selector:"[pTemplateWrapper]"}),i("design:paramtypes",[o.ViewContainerRef])],TemplateWrapper)}();t.TemplateWrapper=u;var p=function(){function Column(){this.sortFunction=new o.EventEmitter}return r([o.Input(),i("design:type",String)],Column.prototype,"field",void 0),r([o.Input(),i("design:type",String)],Column.prototype,"header",void 0),r([o.Input(),i("design:type",String)],Column.prototype,"footer",void 0),r([o.Input(),i("design:type",Object)],Column.prototype,"sortable",void 0),r([o.Input(),i("design:type",Boolean)],Column.prototype,"editable",void 0),r([o.Input(),i("design:type",Boolean)],Column.prototype,"filter",void 0),r([o.Input(),i("design:type",String)],Column.prototype,"filterMatchMode",void 0),r([o.Input(),i("design:type",Number)],Column.prototype,"rowspan",void 0),r([o.Input(),i("design:type",Number)],Column.prototype,"colspan",void 0),r([o.Input(),i("design:type",Object)],Column.prototype,"style",void 0),r([o.Input(),i("design:type",String)],Column.prototype,"styleClass",void 0),r([o.Input(),i("design:type",Boolean)],Column.prototype,"hidden",void 0),r([o.Input(),i("design:type",Boolean)],Column.prototype,"expander",void 0),r([o.Input(),i("design:type",String)],Column.prototype,"selectionMode",void 0),r([o.Output(),i("design:type",o.EventEmitter)],Column.prototype,"sortFunction",void 0),r([o.ContentChild(o.TemplateRef),i("design:type",o.TemplateRef)],Column.prototype,"template",void 0),Column=r([a.Component({selector:"p-column",template:""}),i("design:paramtypes",[])],Column)}();t.Column=p;var d=function(){function ColumnTemplateLoader(e){this.viewContainer=e}return ColumnTemplateLoader.prototype.ngOnInit=function(){this.viewContainer.createEmbeddedView(this.column.template,{$implicit:this.column,rowData:this.rowData,rowIndex:this.rowIndex})},r([o.Input(),i("design:type",Object)],ColumnTemplateLoader.prototype,"column",void 0),r([o.Input(),i("design:type",Object)],ColumnTemplateLoader.prototype,"rowData",void 0),r([o.Input(),i("design:type",Number)],ColumnTemplateLoader.prototype,"rowIndex",void 0),ColumnTemplateLoader=r([a.Component({selector:"p-columnTemplateLoader",template:""}),i("design:paramtypes",[o.ViewContainerRef])],ColumnTemplateLoader)}();t.ColumnTemplateLoader=d;var h=function(){function SharedModule(){}return SharedModule=r([o.NgModule({imports:[s.CommonModule],exports:[l,c,p,u,d],declarations:[l,c,p,u,d]}),i("design:paramtypes",[])],SharedModule)}();t.SharedModule=h},function(e,t,n){"use strict";var r=this&&this.__extends||function(e,t){function __(){this.constructor=e}for(var n in t)t.hasOwnProperty(n)&&(e[n]=t[n]);e.prototype=null===t?Object.create(t):(__.prototype=t.prototype,new __)},i=n(1),o=n(6),s=n(42),a=n(900),l=n(212),c=n(518),u=n(318),p=function(e){function Subject(t,n){e.call(this),this.destination=t,this.source=n,this.observers=[],this.isUnsubscribed=!1,this.isStopped=!1,this.hasErrored=!1,this.dispatching=!1,this.hasCompleted=!1,this.source=n}return r(Subject,e),Subject.prototype.lift=function(e){var t=new Subject(this.destination||this,this);return t.operator=e,t},Subject.prototype.add=function(e){return s.Subscription.prototype.add.call(this,e)},Subject.prototype.remove=function(e){s.Subscription.prototype.remove.call(this,e)},Subject.prototype.unsubscribe=function(){s.Subscription.prototype.unsubscribe.call(this)},Subject.prototype._subscribe=function(e){if(this.source)return this.source.subscribe(e);if(!e.isUnsubscribed){if(this.hasErrored)return e.error(this.errorValue);if(this.hasCompleted)return e.complete();this.throwIfUnsubscribed();var t=new a.SubjectSubscription(this,e);return this.observers.push(e),t}},Subject.prototype._unsubscribe=function(){this.source=null,this.isStopped=!0,this.observers=null,this.destination=null},Subject.prototype.next=function(e){this.throwIfUnsubscribed(),this.isStopped||(this.dispatching=!0,this._next(e),this.dispatching=!1,this.hasErrored?this._error(this.errorValue):this.hasCompleted&&this._complete())},Subject.prototype.error=function(e){this.throwIfUnsubscribed(),this.isStopped||(this.isStopped=!0,this.hasErrored=!0,this.errorValue=e,this.dispatching||this._error(e))},Subject.prototype.complete=function(){this.throwIfUnsubscribed(),this.isStopped||(this.isStopped=!0,this.hasCompleted=!0,this.dispatching||this._complete())},Subject.prototype.asObservable=function(){var e=new d(this);return e},Subject.prototype._next=function(e){this.destination?this.destination.next(e):this._finalNext(e)},Subject.prototype._finalNext=function(e){for(var t=-1,n=this.observers.slice(0),r=n.length;++t"+i+""};e.exports=function(e,t){var n={};n[e]=t(a),r(r.P+r.F*i(function(){var t=""[e]('"');return t!==t.toLowerCase()||t.split('"').length>3}),"String",n)}},function(e,t,n){"use strict";var r=n(82),i=n(515),o=n(214),s=n(43),a=n(38),l=n(514),c=function(){function Subscription(e){this.isUnsubscribed=!1,e&&(this._unsubscribe=e)}return Subscription.prototype.unsubscribe=function(){var e,t=!1;if(!this.isUnsubscribed){this.isUnsubscribed=!0;var n=this,c=n._unsubscribe,u=n._subscriptions;if(this._subscriptions=null,o.isFunction(c)){var p=s.tryCatch(c).call(this);p===a.errorObject&&(t=!0,(e=e||[]).push(a.errorObject.e))}if(r.isArray(u))for(var d=-1,h=u.length;++d0?i(r(e),9007199254740991):0}},function(e,t,n){"use strict";var r=n(1083);t.async=new r.AsyncScheduler},function(e,t,n){"use strict";function __export(e){for(var n in e)t.hasOwnProperty(n)||(t[n]=e[n])}var r=n(89);t.HostMetadata=r.HostMetadata,t.InjectMetadata=r.InjectMetadata,t.InjectableMetadata=r.InjectableMetadata,t.OptionalMetadata=r.OptionalMetadata,t.SelfMetadata=r.SelfMetadata,t.SkipSelfMetadata=r.SkipSelfMetadata,__export(n(117));var i=n(165);t.forwardRef=i.forwardRef,t.resolveForwardRef=i.resolveForwardRef;var o=n(166);t.Injector=o.Injector;var s=n(619);t.ReflectiveInjector=s.ReflectiveInjector;var a=n(253);t.Binding=a.Binding,t.ProviderBuilder=a.ProviderBuilder,t.bind=a.bind,t.Provider=a.Provider,t.provide=a.provide;var l=n(256);t.ResolvedReflectiveFactory=l.ResolvedReflectiveFactory;var c=n(255);t.ReflectiveKey=c.ReflectiveKey;var u=n(254);t.NoProviderError=u.NoProviderError,t.AbstractProviderError=u.AbstractProviderError,t.CyclicDependencyError=u.CyclicDependencyError,t.InstantiationError=u.InstantiationError,t.InvalidProviderError=u.InvalidProviderError,t.NoAnnotationError=u.NoAnnotationError,t.OutOfBoundsError=u.OutOfBoundsError;var p=n(393);t.OpaqueToken=p.OpaqueToken},[1094,32],function(e,t,n){var r=n(14);e.exports=function(e,t){return!!e&&r(function(){t?e.call(null,function(){},1):e.call(null)})}},function(e,t,n){var r=n(133),i=n(65);e.exports=function(e){return r(i(e))}},function(e,t,n){var r=n(65);e.exports=function(e){return Object(r(e))}},function(e,t,n){"use strict";(function(e,n){var r={"boolean":!1,"function":!0,object:!0,number:!1,string:!1,undefined:!1};t.root=r[typeof self]&&self||r[typeof window]&&window;var i=(r[typeof t]&&t&&!t.nodeType&&t,r[typeof e]&&e&&!e.nodeType&&e,r[typeof n]&&n);!i||i.global!==i&&i.window!==i||(t.root=i)}).call(t,n(571)(e),n(70))},function(e,t,n){"use strict";var r=n(0);t.NG_VALUE_ACCESSOR=new r.OpaqueToken("NgValueAccessor")},53,function(e,t,n){"use strict";function _convertToPromise(e){return s.isPromise(e)?e:i.toPromise.call(e)}function _executeValidators(e,t){return t.map(function(t){return t(e)})}function _executeAsyncValidators(e,t){return t.map(function(t){return t(e)})}function _mergeErrors(e){var t=e.reduce(function(e,t){return s.isPresent(t)?o.StringMapWrapper.merge(e,t):e},{});return o.StringMapWrapper.isEmpty(t)?null:t}var r=n(0),i=n(314),o=n(48),s=n(32);t.NG_VALIDATORS=new r.OpaqueToken("NgValidators"),t.NG_ASYNC_VALIDATORS=new r.OpaqueToken("NgAsyncValidators");var a=function(){function Validators(){}return Validators.required=function(e){return s.isBlank(e.value)||s.isString(e.value)&&""==e.value?{required:!0}:null},Validators.minLength=function(e){return function(t){if(s.isPresent(Validators.required(t)))return null;var n=t.value;return n.lengthe?{maxlength:{requiredLength:e,actualLength:n.length}}:null}},Validators.pattern=function(e){return function(t){if(s.isPresent(Validators.required(t)))return null;var n=new RegExp("^"+e+"$"),r=t.value;return n.test(r)?null:{pattern:{requiredPattern:"^"+e+"$",actualValue:r}}}},Validators.nullValidator=function(e){return null},Validators.compose=function(e){if(s.isBlank(e))return null;var t=e.filter(s.isPresent);return 0==t.length?null:function(e){return _mergeErrors(_executeValidators(e,t))}},Validators.composeAsync=function(e){if(s.isBlank(e))return null;var t=e.filter(s.isPresent);return 0==t.length?null:function(e){var n=_executeAsyncValidators(e,t).map(_convertToPromise);return Promise.all(n).then(_mergeErrors)}},Validators}();t.Validators=a},function(e,t,n){"use strict";var r=n(0),i=n(125),o=n(75),s=n(15),a=n(128),l=n(281);t.PRIMITIVE=String;var c=function(){function Serializer(e){this._renderStore=e}return Serializer.prototype.serialize=function(e,n){var i=this;if(!s.isPresent(e))return null;if(s.isArray(e))return e.map(function(e){return i.serialize(e,n)});if(n==t.PRIMITIVE)return e;if(n==u)return this._renderStore.serialize(e);if(n===r.RenderComponentType)return this._serializeRenderComponentType(e);if(n===r.ViewEncapsulation)return s.serializeEnum(e);if(n===l.LocationType)return this._serializeLocation(e);throw new o.BaseException("No serializer for "+n.toString())},Serializer.prototype.deserialize=function(e,n,a){var c=this;if(!s.isPresent(e))return null;if(s.isArray(e)){var p=[];return e.forEach(function(e){return p.push(c.deserialize(e,n,a))}),p}if(n==t.PRIMITIVE)return e;if(n==u)return this._renderStore.deserialize(e);if(n===r.RenderComponentType)return this._deserializeRenderComponentType(e);if(n===r.ViewEncapsulation)return i.VIEW_ENCAPSULATION_VALUES[e];if(n===l.LocationType)return this._deserializeLocation(e);throw new o.BaseException("No deserializer for "+n.toString())},Serializer.prototype._serializeLocation=function(e){return{href:e.href,protocol:e.protocol,host:e.host,hostname:e.hostname,port:e.port,pathname:e.pathname,search:e.search,hash:e.hash,origin:e.origin}},Serializer.prototype._deserializeLocation=function(e){return new l.LocationType(e.href,e.protocol,e.host,e.hostname,e.port,e.pathname,e.search,e.hash,e.origin)},Serializer.prototype._serializeRenderComponentType=function(e){return{id:e.id,templateUrl:e.templateUrl,slotCount:e.slotCount,encapsulation:this.serialize(e.encapsulation,r.ViewEncapsulation),styles:this.serialize(e.styles,t.PRIMITIVE)}},Serializer.prototype._deserializeRenderComponentType=function(e){return new r.RenderComponentType(e.id,e.templateUrl,e.slotCount,this.deserialize(e.encapsulation,r.ViewEncapsulation),this.deserialize(e.styles,t.PRIMITIVE),{})},Serializer.decorators=[{type:r.Injectable}],Serializer.ctorParameters=[{type:a.RenderStore}],Serializer}();t.Serializer=c;var u=function(){function RenderStoreObject(){}return RenderStoreObject}();t.RenderStoreObject=u},function(e,t,n){var r=n(2),i=n(25),o=n(14);e.exports=function(e,t){var n=(i.Object||{})[e]||Object[e],s={};s[e]=t(n),r(r.S+r.F*o(function(){n(1)}),"Object",s)}},function(e,t,n){"use strict";function _convertToPromise(e){return s.isPromise(e)?e:i.toPromise.call(e)}function _executeValidators(e,t){return t.map(function(t){return t(e)})}function _executeAsyncValidators(e,t){return t.map(function(t){return t(e)})}function _mergeErrors(e){var t=e.reduce(function(e,t){return s.isPresent(t)?o.StringMapWrapper.merge(e,t):e},{});return o.StringMapWrapper.isEmpty(t)?null:t}var r=n(0),i=n(314),o=n(34),s=n(7);t.NG_VALIDATORS=new r.OpaqueToken("NgValidators"),t.NG_ASYNC_VALIDATORS=new r.OpaqueToken("NgAsyncValidators");var a=function(){function Validators(){}return Validators.required=function(e){return s.isBlank(e.value)||s.isString(e.value)&&""==e.value?{required:!0}:null},Validators.minLength=function(e){return function(t){if(s.isPresent(Validators.required(t)))return null;var n=t.value;return n.lengthe?{maxlength:{requiredLength:e,actualLength:n.length}}:null}},Validators.pattern=function(e){return function(t){if(s.isPresent(Validators.required(t)))return null;var n=new RegExp("^"+e+"$"),r=t.value;return n.test(r)?null:{pattern:{requiredPattern:"^"+e+"$",actualValue:r}}}},Validators.nullValidator=function(e){return null},Validators.compose=function(e){if(s.isBlank(e))return null;var t=e.filter(s.isPresent);return 0==t.length?null:function(e){return _mergeErrors(_executeValidators(e,t))}},Validators.composeAsync=function(e){if(s.isBlank(e))return null;var t=e.filter(s.isPresent);return 0==t.length?null:function(e){var n=_executeAsyncValidators(e,t).map(_convertToPromise);return Promise.all(n).then(_mergeErrors)}},Validators}();t.Validators=a},function(e,t,n){"use strict";var r=this&&this.__extends||function(e,t){function __(){this.constructor=e}for(var n in t)t.hasOwnProperty(n)&&(e[n]=t[n]);e.prototype=null===t?Object.create(t):(__.prototype=t.prototype,new __)},i=n(86),o=n(7),s=function(e){function InvalidPipeArgumentException(t,n){e.call(this,"Invalid argument '"+n+"' for pipe '"+o.stringify(t)+"'")}return r(InvalidPipeArgumentException,e),InvalidPipeArgumentException}(i.BaseException);t.InvalidPipeArgumentException=s},function(e,t,n){"use strict";/** +webpackJsonp([0,2],[function(e,t,n){"use strict";function __export(e){for(var n in e)t.hasOwnProperty(n)||(t[n]=e[n])}__export(n(406)),__export(n(641)),__export(n(47));var r=n(251);t.createPlatform=r.createPlatform,t.assertPlatform=r.assertPlatform,t.disposePlatform=r.disposePlatform,t.getPlatform=r.getPlatform,t.coreBootstrap=r.coreBootstrap,t.coreLoadAndBootstrap=r.coreLoadAndBootstrap,t.PlatformRef=r.PlatformRef,t.ApplicationRef=r.ApplicationRef,t.enableProdMode=r.enableProdMode,t.lockRunMode=r.lockRunMode,t.isDevMode=r.isDevMode,t.createPlatformFactory=r.createPlatformFactory;var i=n(161);t.APP_ID=i.APP_ID,t.PACKAGE_ROOT_URL=i.PACKAGE_ROOT_URL,t.PLATFORM_INITIALIZER=i.PLATFORM_INITIALIZER,t.APP_BOOTSTRAP_LISTENER=i.APP_BOOTSTRAP_LISTENER;var o=n(250);t.APP_INITIALIZER=o.APP_INITIALIZER,t.ApplicationInitStatus=o.ApplicationInitStatus,__export(n(642)),__export(n(640)),__export(n(629));var s=n(393);t.DebugElement=s.DebugElement,t.DebugNode=s.DebugNode,t.asNativeElements=s.asNativeElements,t.getDebugNode=s.getDebugNode,__export(n(264)),__export(n(624)),__export(n(637)),__export(n(636));var a=n(623);t.APPLICATION_COMMON_PROVIDERS=a.APPLICATION_COMMON_PROVIDERS,t.ApplicationModule=a.ApplicationModule;var l=n(171);t.wtfCreateScope=l.wtfCreateScope,t.wtfLeave=l.wtfLeave,t.wtfStartTimeRange=l.wtfStartTimeRange,t.wtfEndTimeRange=l.wtfEndTimeRange;var c=n(4);t.Type=c.Type;var u=n(257);t.EventEmitter=u.EventEmitter;var p=n(13);t.ExceptionHandler=p.ExceptionHandler,t.WrappedException=p.WrappedException,t.BaseException=p.BaseException,__export(n(617)),__export(n(389));var d=n(249);t.AnimationPlayer=d.AnimationPlayer;var h=n(414);t.SanitizationService=h.SanitizationService,t.SecurityContext=h.SecurityContext},function(e,t,n){"use strict";var r=n(52),i=n(212),o=n(1098),s=function(){function Observable(e){this._isScalar=!1,e&&(this._subscribe=e)}return Observable.prototype.lift=function(e){var t=new Observable;return t.source=this,t.operator=e,t},Observable.prototype.subscribe=function(e,t,n){var r=this.operator,i=o.toSubscriber(e,t,n);if(i.add(r?r.call(i,this):this._subscribe(i)),i.syncErrorThrowable&&(i.syncErrorThrowable=!1,i.syncErrorThrown))throw i.syncErrorValue;return i},Observable.prototype.forEach=function(e,t){var n=this;if(t||(r.root.Rx&&r.root.Rx.config&&r.root.Rx.config.Promise?t=r.root.Rx.config.Promise:r.root.Promise&&(t=r.root.Promise)),!t)throw new Error("no Promise impl found");return new t(function(t,r){var i=n.subscribe(function(t){if(i)try{e(t)}catch(n){r(n),i.unsubscribe()}else e(t)},r,t)})},Observable.prototype._subscribe=function(e){return this.source.subscribe(e)},Observable.prototype[i.$$observable]=function(){return this},Observable.create=function(e){return new Observable(e)},Observable}();t.Observable=s},function(e,t,n){var r=n(26),i=n(25),o=n(66),s=n(40),a=n(107),l="prototype",c=function(e,t,n){var u,p,d,h,f=e&c.F,m=e&c.G,y=e&c.S,v=e&c.P,g=e&c.B,b=m?r:y?r[t]||(r[t]={}):(r[t]||{})[l],_=m?i:i[t]||(i[t]={}),S=_[l]||(_[l]={});m&&(n=t);for(u in n)p=!f&&b&&void 0!==b[u],d=(p?b:n)[u],h=g&&p?a(d,r):v&&"function"==typeof d?a(Function.call,d):d,b&&s(b,u,d,e&c.U),_[u]!=d&&o(_,u,h),v&&S[u]!=d&&(S[u]=d)};r.core=i,c.F=1,c.G=2,c.S=4,c.P=8,c.B=16,c.W=32,c.U=64,c.R=128,e.exports=c},function(e,t,n){"use strict";function __export(e){for(var n in e)t.hasOwnProperty(n)||(t[n]=e[n])}var r=n(0),i=n(341),o=n(354);__export(n(354)),__export(n(342)),__export(n(581)),__export(n(341)),__export(n(583));var s=n(233);t.NgLocalization=s.NgLocalization;var a=function(){function CommonModule(){}return CommonModule.decorators=[{type:r.NgModule,args:[{declarations:[i.COMMON_DIRECTIVES,o.COMMON_PIPES],exports:[i.COMMON_DIRECTIVES,o.COMMON_PIPES]}]}],CommonModule}();t.CommonModule=a},function(e,t,n){"use strict";(function(e){function scheduleMicroTask(e){Zone.current.scheduleMicroTask("scheduleMicrotask",e)}function getTypeNameForDebugging(e){return e.name?e.name:typeof e}function isPresent(e){return void 0!==e&&null!==e}function isBlank(e){return void 0===e||null===e}function isBoolean(e){return"boolean"==typeof e}function isNumber(e){return"number"==typeof e}function isString(e){return"string"==typeof e}function isFunction(e){return"function"==typeof e}function isType(e){return isFunction(e)}function isStringMap(e){return"object"==typeof e&&null!==e}function isStrictStringMap(e){return isStringMap(e)&&Object.getPrototypeOf(e)===o}function isPromise(e){return isPresent(e)&&isFunction(e.then)}function isArray(e){return Array.isArray(e)}function isDate(e){return e instanceof t.Date&&!isNaN(e.valueOf())}function noop(){}function stringify(e){if("string"==typeof e)return e;if(void 0===e||null===e)return""+e;if(e.overriddenName)return e.overriddenName;if(e.name)return e.name;var t=e.toString(),n=t.indexOf("\n");return n===-1?t:t.substring(0,n)}function serializeEnum(e){return e}function deserializeEnum(e,t){return e}function resolveEnumToken(e,t){return e[t]}function looseIdentical(e,t){return e===t||"number"==typeof e&&"number"==typeof t&&isNaN(e)&&isNaN(t)}function getMapKey(e){return e}function normalizeBlank(e){return isBlank(e)?null:e}function normalizeBool(e){return!isBlank(e)&&e}function isJsObject(e){return null!==e&&("function"==typeof e||"object"==typeof e)}function print(e){console.log(e)}function warn(e){console.warn(e)}function setValueOnPath(e,t,n){for(var r=t.split("."),i=e;r.length>1;){var o=r.shift();i=i.hasOwnProperty(o)&&isPresent(i[o])?i[o]:i[o]={}}void 0!==i&&null!==i||(i={}),i[r.shift()]=n}function getSymbolIterator(){if(isBlank(h))if(isPresent(n.Symbol)&&isPresent(Symbol.iterator))h=Symbol.iterator;else for(var e=Object.getOwnPropertyNames(Map.prototype),t=0;t=0&&e[r]==t;r--)n--;e=e.substring(0,n)}return e},StringWrapper.replace=function(e,t,n){return e.replace(t,n)},StringWrapper.replaceAll=function(e,t,n){return e.replace(t,n)},StringWrapper.slice=function(e,t,n){return void 0===t&&(t=0),void 0===n&&(n=null),e.slice(t,null===n?void 0:n)},StringWrapper.replaceAllMapped=function(e,t,n){return e.replace(t,function(){for(var e=[],t=0;tt?1:0},StringWrapper}();t.StringWrapper=s;var a=function(){function StringJoiner(e){void 0===e&&(e=[]),this.parts=e}return StringJoiner.prototype.add=function(e){this.parts.push(e)},StringJoiner.prototype.toString=function(){return this.parts.join("")},StringJoiner}();t.StringJoiner=a;var l=function(e){function NumberParseError(t){e.call(this),this.message=t}return r(NumberParseError,e),NumberParseError.prototype.toString=function(){return this.message},NumberParseError}(Error);t.NumberParseError=l;var c=function(){function NumberWrapper(){}return NumberWrapper.toFixed=function(e,t){return e.toFixed(t)},NumberWrapper.equal=function(e,t){return e===t},NumberWrapper.parseIntAutoRadix=function(e){var t=parseInt(e);if(isNaN(t))throw new l("Invalid integer literal when parsing "+e);return t},NumberWrapper.parseInt=function(e,t){if(10==t){if(/^(\-|\+)?[0-9]+$/.test(e))return parseInt(e,t)}else if(16==t){if(/^(\-|\+)?[0-9ABCDEFabcdef]+$/.test(e))return parseInt(e,t)}else{var n=parseInt(e,t);if(!isNaN(n))return n}throw new l("Invalid integer literal when parsing "+e+" in base "+t)},NumberWrapper.parseFloat=function(e){return parseFloat(e)},Object.defineProperty(NumberWrapper,"NaN",{get:function(){return NaN},enumerable:!0,configurable:!0}),NumberWrapper.isNumeric=function(e){return!isNaN(e-parseFloat(e))},NumberWrapper.isNaN=function(e){return isNaN(e)},NumberWrapper.isInteger=function(e){return Number.isInteger(e)},NumberWrapper}();t.NumberWrapper=c,t.RegExp=i.RegExp;var u=function(){function FunctionWrapper(){}return FunctionWrapper.apply=function(e,t){return e.apply(null,t)},FunctionWrapper.bind=function(e,t){return e.bind(t)},FunctionWrapper}();t.FunctionWrapper=u,t.looseIdentical=looseIdentical,t.getMapKey=getMapKey,t.normalizeBlank=normalizeBlank,t.normalizeBool=normalizeBool,t.isJsObject=isJsObject,t.print=print,t.warn=warn;var p=function(){function Json(){}return Json.parse=function(e){return i.JSON.parse(e)},Json.stringify=function(e){return i.JSON.stringify(e,null,2)},Json}();t.Json=p;var d=function(){function DateWrapper(){}return DateWrapper.create=function(e,n,r,i,o,s,a){return void 0===n&&(n=1),void 0===r&&(r=1),void 0===i&&(i=0),void 0===o&&(o=0),void 0===s&&(s=0),void 0===a&&(a=0),new t.Date(e,n-1,r,i,o,s,a)},DateWrapper.fromISOString=function(e){return new t.Date(e)},DateWrapper.fromMillis=function(e){return new t.Date(e)},DateWrapper.toMillis=function(e){return e.getTime()},DateWrapper.now=function(){return new t.Date},DateWrapper.toJson=function(e){return e.toJSON()},DateWrapper}();t.DateWrapper=d,t.setValueOnPath=setValueOnPath;var h=null;t.getSymbolIterator=getSymbolIterator,t.evalExpression=evalExpression,t.isPrimitive=isPrimitive,t.hasConstructor=hasConstructor,t.escape=escape,t.escapeRegExp=escapeRegExp}).call(t,n(70))},4,function(e,t,n){"use strict";var r=this&&this.__extends||function(e,t){function __(){this.constructor=e}for(var n in t)t.hasOwnProperty(n)&&(e[n]=t[n]);e.prototype=null===t?Object.create(t):(__.prototype=t.prototype,new __)},i=n(215),o=n(42),s=n(213),a=n(906),l=function(e){function Subscriber(t,n,r){switch(e.call(this),this.syncErrorValue=null,this.syncErrorThrown=!1,this.syncErrorThrowable=!1,this.isStopped=!1,arguments.length){case 0:this.destination=a.empty;break;case 1:if(!t){this.destination=a.empty;break}if("object"==typeof t){t instanceof Subscriber?(this.destination=t,this.destination.add(this)):(this.syncErrorThrowable=!0,this.destination=new c(this,t));break}default:this.syncErrorThrowable=!0,this.destination=new c(this,t,n,r)}}return r(Subscriber,e),Subscriber.create=function(e,t,n){var r=new Subscriber(e,t,n);return r.syncErrorThrowable=!1,r},Subscriber.prototype.next=function(e){this.isStopped||this._next(e)},Subscriber.prototype.error=function(e){this.isStopped||(this.isStopped=!0,this._error(e))},Subscriber.prototype.complete=function(){this.isStopped||(this.isStopped=!0,this._complete())},Subscriber.prototype.unsubscribe=function(){this.isUnsubscribed||(this.isStopped=!0,e.prototype.unsubscribe.call(this))},Subscriber.prototype._next=function(e){this.destination.next(e)},Subscriber.prototype._error=function(e){this.destination.error(e),this.unsubscribe()},Subscriber.prototype._complete=function(){this.destination.complete(),this.unsubscribe()},Subscriber.prototype[s.$$rxSubscriber]=function(){return this},Subscriber}(o.Subscription);t.Subscriber=l;var c=function(e){function SafeSubscriber(t,n,r,o){e.call(this),this._parent=t;var s,a=this;i.isFunction(n)?s=n:n&&(a=n,s=n.next,r=n.error,o=n.complete,i.isFunction(a.unsubscribe)&&this.add(a.unsubscribe.bind(a)),a.unsubscribe=this.unsubscribe.bind(this)),this._context=a,this._next=s,this._error=r,this._complete=o}return r(SafeSubscriber,e),SafeSubscriber.prototype.next=function(e){if(!this.isStopped&&this._next){var t=this._parent;t.syncErrorThrowable?this.__tryOrSetError(t,this._next,e)&&this.unsubscribe():this.__tryOrUnsub(this._next,e)}},SafeSubscriber.prototype.error=function(e){if(!this.isStopped){var t=this._parent;if(this._error)t.syncErrorThrowable?(this.__tryOrSetError(t,this._error,e),this.unsubscribe()):(this.__tryOrUnsub(this._error,e),this.unsubscribe());else{if(!t.syncErrorThrowable)throw this.unsubscribe(),e;t.syncErrorValue=e,t.syncErrorThrown=!0,this.unsubscribe()}}},SafeSubscriber.prototype.complete=function(){if(!this.isStopped){var e=this._parent;this._complete?e.syncErrorThrowable?(this.__tryOrSetError(e,this._complete),this.unsubscribe()):(this.__tryOrUnsub(this._complete),this.unsubscribe()):this.unsubscribe()}},SafeSubscriber.prototype.__tryOrUnsub=function(e,t){try{e.call(this._context,t)}catch(n){throw this.unsubscribe(),n}},SafeSubscriber.prototype.__tryOrSetError=function(e,t,n){try{t.call(this._context,n)}catch(r){return e.syncErrorValue=r,e.syncErrorThrown=!0,!0}return!1},SafeSubscriber.prototype._unsubscribe=function(){var e=this._parent;this._context=null,this._parent=null,e.unsubscribe()},SafeSubscriber}(l)},4,function(e,t,n){var r=n(17);e.exports=function(e){if(!r(e))throw TypeError(e+" is not an object!");return e}},function(e,t,n){"use strict";var r=this&&this.__decorate||function(e,t,n,r){var i,o=arguments.length,s=o<3?t:null===r?r=Object.getOwnPropertyDescriptor(t,n):r;if("object"==typeof Reflect&&"function"==typeof Reflect.decorate)s=Reflect.decorate(e,t,n,r);else for(var a=e.length-1;a>=0;a--)(i=e[a])&&(s=(o<3?i(s):o>3?i(t,n,s):i(t,n))||s);return o>3&&s&&Object.defineProperty(t,n,s),s},i=this&&this.__metadata||function(e,t){if("object"==typeof Reflect&&"function"==typeof Reflect.metadata)return Reflect.metadata(e,t)},o=n(0),s=function(){function DomHandler(){}return DomHandler.prototype.addClass=function(e,t){e.classList?e.classList.add(t):e.className+=" "+t},DomHandler.prototype.addMultipleClasses=function(e,t){if(e.classList)for(var n=t.split(" "),r=0;rwindow.innerHeight?-1*i.height:o,r=a.left+i.width>window.innerWidth?s-i.width:0,e.style.top=n+"px",e.style.left=r+"px"},DomHandler.prototype.absolutePosition=function(e,t){var n,r,i=e.offsetParent?{width:e.offsetWidth,height:e.offsetHeight}:this.getHiddenElementDimensions(e),o=i.height,s=i.width,a=t.offsetHeight,l=t.offsetWidth,c=t.getBoundingClientRect(),u=this.getWindowScrollTop(),p=this.getWindowScrollLeft();n=c.top+a+o>window.innerHeight?c.top+u-o:a+c.top+u,r=c.left+l+s>window.innerWidth?c.left+p+l-s:c.left+p,e.style.top=n+"px",e.style.left=r+"px"},DomHandler.prototype.getHiddenElementOuterHeight=function(e){e.style.visibility="hidden",e.style.display="block";var t=e.offsetHeight;return e.style.display="none",e.style.visibility="visible",t},DomHandler.prototype.getHiddenElementOuterWidth=function(e){e.style.visibility="hidden",e.style.display="block";var t=e.offsetWidth;return e.style.display="none",e.style.visibility="visible",t},DomHandler.prototype.getHiddenElementDimensions=function(e){var t={};return e.style.visibility="hidden",e.style.display="block",t.width=e.offsetWidth,t.height=e.offsetHeight,e.style.display="none",e.style.visibility="visible",t},DomHandler.prototype.scrollInView=function(e,t){var n=getComputedStyle(e).getPropertyValue("borderTopWidth"),r=n?parseFloat(n):0,i=getComputedStyle(e).getPropertyValue("paddingTop"),o=i?parseFloat(i):0,s=e.getBoundingClientRect(),a=t.getBoundingClientRect(),l=a.top+document.body.scrollTop-(s.top+document.body.scrollTop)-r-o,c=e.scrollTop,u=e.clientHeight,p=this.getOuterHeight(t);l<0?e.scrollTop=c+l:l+p>u&&(e.scrollTop=c+l-u+p)},DomHandler.prototype.fadeIn=function(e,t){e.style.opacity=0;var n=+new Date,r=function(){e.style.opacity=+e.style.opacity+((new Date).getTime()-n)/t,n=+new Date,+e.style.opacity<1&&(window.requestAnimationFrame&&requestAnimationFrame(r)||setTimeout(r,16))};r()},DomHandler.prototype.fadeOut=function(e,t){var n=1,r=50,i=t,o=r/i,s=setInterval(function(){n-=o,e.style.opacity=n,n<=0&&clearInterval(s)},r)},DomHandler.prototype.getWindowScrollTop=function(){var e=document.documentElement;return(window.pageYOffset||e.scrollTop)-(e.clientTop||0)},DomHandler.prototype.getWindowScrollLeft=function(){var e=document.documentElement;return(window.pageXOffset||e.scrollLeft)-(e.clientLeft||0)},DomHandler.prototype.matches=function(e,t){var n=Element.prototype,r=n.matches||n.webkitMatchesSelector||n.mozMatchesSelector||n.msMatchesSelector||function(e){return[].indexOf.call(document.querySelectorAll(e),this)!==-1};return r.call(e,t)},DomHandler.prototype.getOuterWidth=function(e,t){var n=e.offsetWidth;if(t){var r=getComputedStyle(e);n+=parseInt(r.paddingLeft)+parseInt(r.paddingRight)}return n},DomHandler.prototype.getHorizontalMargin=function(e){var t=getComputedStyle(e);return parseInt(t.marginLeft)+parseInt(t.marginRight)},DomHandler.prototype.innerWidth=function(e){var t=e.offsetWidth,n=getComputedStyle(e);return t+=parseInt(n.paddingLeft)+parseInt(n.paddingRight)},DomHandler.prototype.width=function(e){var t=e.offsetWidth,n=getComputedStyle(e);return t-=parseInt(n.paddingLeft)+parseInt(n.paddingRight)},DomHandler.prototype.getOuterHeight=function(e,t){var n=e.offsetHeight;if(t){var r=getComputedStyle(e);n+=parseInt(r.marginTop)+parseInt(r.marginBottom)}return n},DomHandler.prototype.getHeight=function(e){var t=e.offsetHeight,n=getComputedStyle(e);return t-=parseInt(n.paddingTop)+parseInt(n.paddingBottom)+parseInt(n.borderTopWidth)+parseInt(n.borderBottomWidth)},DomHandler.prototype.getViewport=function(){var e=window,t=document,n=t.documentElement,r=t.getElementsByTagName("body")[0],i=e.innerWidth||n.clientWidth||r.clientWidth,o=e.innerHeight||n.clientHeight||r.clientHeight;return{width:i,height:o}},DomHandler.prototype.equals=function(e,t){if(null==e||null==t)return!1;if(e==t)return!0;if("object"==typeof e&&"object"==typeof t){for(var n in e){if(e.hasOwnProperty(n)!==t.hasOwnProperty(n))return!1;switch(typeof e[n]){case"object":if(!this.equals(e[n],t[n]))return!1;break;case"function":if("undefined"==typeof t[n]||"compare"!=n&&e[n].toString()!=t[n].toString())return!1;break;default:if(e[n]!=t[n])return!1}}for(var n in t)if("undefined"==typeof e[n])return!1;return!0}return!1},DomHandler.zindex=1e3,DomHandler=r([o.Injectable(),i("design:paramtypes",[])],DomHandler)}();t.DomHandler=s},[1102,5],function(e,t,n){"use strict";var r=this&&this.__extends||function(e,t){function __(){this.constructor=e}for(var n in t)t.hasOwnProperty(n)&&(e[n]=t[n]);e.prototype=null===t?Object.create(t):(__.prototype=t.prototype,new __)},i=n(6),o=function(e){function OuterSubscriber(){e.apply(this,arguments)}return r(OuterSubscriber,e),OuterSubscriber.prototype.notifyNext=function(e,t,n,r,i){this.destination.next(t)},OuterSubscriber.prototype.notifyError=function(e,t){this.destination.error(e)},OuterSubscriber.prototype.notifyComplete=function(e){this.destination.complete()},OuterSubscriber}(i.Subscriber);t.OuterSubscriber=o},function(e,t,n){"use strict";function subscribeToResult(e,t,n,u){var p=new c.InnerSubscriber(e,n,u);if(!p.isUnsubscribed){if(t instanceof s.Observable)return t._isScalar?(p.next(t.value),void p.complete()):t.subscribe(p);if(i.isArray(t)){for(var d=0,h=t.length;d=0;a--)(i=e[a])&&(s=(o<3?i(s):o>3?i(t,n,s):i(t,n))||s);return o>3&&s&&Object.defineProperty(t,n,s),s},i=this&&this.__metadata||function(e,t){if("object"==typeof Reflect&&"function"==typeof Reflect.metadata)return Reflect.metadata(e,t)},o=n(0),s=n(3),a=n(0),l=function(){function Header(){}return Header=r([a.Component({selector:"header",template:""}),i("design:paramtypes",[])],Header)}();t.Header=l;var c=function(){function Footer(){}return Footer=r([a.Component({selector:"footer",template:""}),i("design:paramtypes",[])],Footer)}();t.Footer=c;var u=function(){function TemplateWrapper(e){this.viewContainer=e}return TemplateWrapper.prototype.ngOnInit=function(){this.viewContainer.createEmbeddedView(this.templateRef,{$implicit:this.item})},r([o.Input(),i("design:type",Object)],TemplateWrapper.prototype,"item",void 0),r([o.Input("pTemplateWrapper"),i("design:type",o.TemplateRef)],TemplateWrapper.prototype,"templateRef",void 0),TemplateWrapper=r([o.Directive({selector:"[pTemplateWrapper]"}),i("design:paramtypes",[o.ViewContainerRef])],TemplateWrapper)}();t.TemplateWrapper=u;var p=function(){function Column(){this.sortFunction=new o.EventEmitter}return r([o.Input(),i("design:type",String)],Column.prototype,"field",void 0),r([o.Input(),i("design:type",String)],Column.prototype,"header",void 0),r([o.Input(),i("design:type",String)],Column.prototype,"footer",void 0),r([o.Input(),i("design:type",Object)],Column.prototype,"sortable",void 0),r([o.Input(),i("design:type",Boolean)],Column.prototype,"editable",void 0),r([o.Input(),i("design:type",Boolean)],Column.prototype,"filter",void 0),r([o.Input(),i("design:type",String)],Column.prototype,"filterMatchMode",void 0),r([o.Input(),i("design:type",Number)],Column.prototype,"rowspan",void 0),r([o.Input(),i("design:type",Number)],Column.prototype,"colspan",void 0),r([o.Input(),i("design:type",Object)],Column.prototype,"style",void 0),r([o.Input(),i("design:type",String)],Column.prototype,"styleClass",void 0),r([o.Input(),i("design:type",Boolean)],Column.prototype,"hidden",void 0),r([o.Input(),i("design:type",Boolean)],Column.prototype,"expander",void 0),r([o.Input(),i("design:type",String)],Column.prototype,"selectionMode",void 0),r([o.Output(),i("design:type",o.EventEmitter)],Column.prototype,"sortFunction",void 0),r([o.ContentChild(o.TemplateRef),i("design:type",o.TemplateRef)],Column.prototype,"template",void 0),Column=r([a.Component({selector:"p-column",template:""}),i("design:paramtypes",[])],Column)}();t.Column=p;var d=function(){function ColumnTemplateLoader(e){this.viewContainer=e}return ColumnTemplateLoader.prototype.ngOnInit=function(){this.viewContainer.createEmbeddedView(this.column.template,{$implicit:this.column,rowData:this.rowData,rowIndex:this.rowIndex})},r([o.Input(),i("design:type",Object)],ColumnTemplateLoader.prototype,"column",void 0),r([o.Input(),i("design:type",Object)],ColumnTemplateLoader.prototype,"rowData",void 0),r([o.Input(),i("design:type",Number)],ColumnTemplateLoader.prototype,"rowIndex",void 0),ColumnTemplateLoader=r([a.Component({selector:"p-columnTemplateLoader",template:""}),i("design:paramtypes",[o.ViewContainerRef])],ColumnTemplateLoader)}();t.ColumnTemplateLoader=d;var h=function(){function SharedModule(){}return SharedModule=r([o.NgModule({imports:[s.CommonModule],exports:[l,c,p,u,d],declarations:[l,c,p,u,d]}),i("design:paramtypes",[])],SharedModule)}();t.SharedModule=h},function(e,t,n){"use strict";var r=this&&this.__extends||function(e,t){function __(){this.constructor=e}for(var n in t)t.hasOwnProperty(n)&&(e[n]=t[n]);e.prototype=null===t?Object.create(t):(__.prototype=t.prototype,new __)},i=n(1),o=n(6),s=n(42),a=n(908),l=n(213),c=n(519),u=n(318),p=function(e){function Subject(t,n){e.call(this),this.destination=t,this.source=n,this.observers=[],this.isUnsubscribed=!1,this.isStopped=!1,this.hasErrored=!1,this.dispatching=!1,this.hasCompleted=!1,this.source=n}return r(Subject,e),Subject.prototype.lift=function(e){var t=new Subject(this.destination||this,this);return t.operator=e,t},Subject.prototype.add=function(e){return s.Subscription.prototype.add.call(this,e)},Subject.prototype.remove=function(e){s.Subscription.prototype.remove.call(this,e)},Subject.prototype.unsubscribe=function(){s.Subscription.prototype.unsubscribe.call(this)},Subject.prototype._subscribe=function(e){if(this.source)return this.source.subscribe(e);if(!e.isUnsubscribed){if(this.hasErrored)return e.error(this.errorValue);if(this.hasCompleted)return e.complete();this.throwIfUnsubscribed();var t=new a.SubjectSubscription(this,e);return this.observers.push(e),t}},Subject.prototype._unsubscribe=function(){this.source=null,this.isStopped=!0,this.observers=null,this.destination=null},Subject.prototype.next=function(e){this.throwIfUnsubscribed(),this.isStopped||(this.dispatching=!0,this._next(e),this.dispatching=!1,this.hasErrored?this._error(this.errorValue):this.hasCompleted&&this._complete())},Subject.prototype.error=function(e){this.throwIfUnsubscribed(),this.isStopped||(this.isStopped=!0,this.hasErrored=!0,this.errorValue=e,this.dispatching||this._error(e))},Subject.prototype.complete=function(){this.throwIfUnsubscribed(),this.isStopped||(this.isStopped=!0,this.hasCompleted=!0,this.dispatching||this._complete())},Subject.prototype.asObservable=function(){var e=new d(this);return e},Subject.prototype._next=function(e){this.destination?this.destination.next(e):this._finalNext(e)},Subject.prototype._finalNext=function(e){for(var t=-1,n=this.observers.slice(0),r=n.length;++t"+i+""};e.exports=function(e,t){var n={};n[e]=t(a),r(r.P+r.F*i(function(){var t=""[e]('"');return t!==t.toLowerCase()||t.split('"').length>3}),"String",n)}},function(e,t,n){"use strict";var r=n(82),i=n(516),o=n(215),s=n(43),a=n(38),l=n(515),c=function(){function Subscription(e){this.isUnsubscribed=!1,e&&(this._unsubscribe=e)}return Subscription.prototype.unsubscribe=function(){var e,t=!1;if(!this.isUnsubscribed){this.isUnsubscribed=!0;var n=this,c=n._unsubscribe,u=n._subscriptions;if(this._subscriptions=null,o.isFunction(c)){var p=s.tryCatch(c).call(this);p===a.errorObject&&(t=!0,(e=e||[]).push(a.errorObject.e))}if(r.isArray(u))for(var d=-1,h=u.length;++d0?i(r(e),9007199254740991):0}},function(e,t,n){"use strict";var r=n(1091);t.async=new r.AsyncScheduler},function(e,t,n){"use strict";function __export(e){for(var n in e)t.hasOwnProperty(n)||(t[n]=e[n])}var r=n(89);t.HostMetadata=r.HostMetadata,t.InjectMetadata=r.InjectMetadata,t.InjectableMetadata=r.InjectableMetadata,t.OptionalMetadata=r.OptionalMetadata,t.SelfMetadata=r.SelfMetadata,t.SkipSelfMetadata=r.SkipSelfMetadata,__export(n(117));var i=n(166);t.forwardRef=i.forwardRef,t.resolveForwardRef=i.resolveForwardRef;var o=n(167);t.Injector=o.Injector;var s=n(627);t.ReflectiveInjector=s.ReflectiveInjector;var a=n(253);t.Binding=a.Binding,t.ProviderBuilder=a.ProviderBuilder,t.bind=a.bind,t.Provider=a.Provider,t.provide=a.provide;var l=n(256);t.ResolvedReflectiveFactory=l.ResolvedReflectiveFactory;var c=n(255);t.ReflectiveKey=c.ReflectiveKey;var u=n(254);t.NoProviderError=u.NoProviderError,t.AbstractProviderError=u.AbstractProviderError,t.CyclicDependencyError=u.CyclicDependencyError,t.InstantiationError=u.InstantiationError,t.InvalidProviderError=u.InvalidProviderError,t.NoAnnotationError=u.NoAnnotationError,t.OutOfBoundsError=u.OutOfBoundsError;var p=n(394);t.OpaqueToken=p.OpaqueToken},[1102,32],function(e,t,n){var r=n(14);e.exports=function(e,t){return!!e&&r(function(){t?e.call(null,function(){},1):e.call(null)})}},function(e,t,n){var r=n(133),i=n(65);e.exports=function(e){return r(i(e))}},function(e,t,n){var r=n(65);e.exports=function(e){return Object(r(e))}},function(e,t,n){"use strict";(function(e,n){var r={"boolean":!1,"function":!0,object:!0,number:!1,string:!1,undefined:!1};t.root=r[typeof self]&&self||r[typeof window]&&window;var i=(r[typeof t]&&t&&!t.nodeType&&t,r[typeof e]&&e&&!e.nodeType&&e,r[typeof n]&&n);!i||i.global!==i&&i.window!==i||(t.root=i)}).call(t,n(579)(e),n(70))},function(e,t,n){"use strict";var r=n(0);t.NG_VALUE_ACCESSOR=new r.OpaqueToken("NgValueAccessor")},53,function(e,t,n){"use strict";function _convertToPromise(e){return s.isPromise(e)?e:i.toPromise.call(e)}function _executeValidators(e,t){return t.map(function(t){return t(e)})}function _executeAsyncValidators(e,t){return t.map(function(t){return t(e)})}function _mergeErrors(e){var t=e.reduce(function(e,t){return s.isPresent(t)?o.StringMapWrapper.merge(e,t):e},{});return o.StringMapWrapper.isEmpty(t)?null:t}var r=n(0),i=n(314),o=n(48),s=n(32);t.NG_VALIDATORS=new r.OpaqueToken("NgValidators"),t.NG_ASYNC_VALIDATORS=new r.OpaqueToken("NgAsyncValidators");var a=function(){function Validators(){}return Validators.required=function(e){return s.isBlank(e.value)||s.isString(e.value)&&""==e.value?{required:!0}:null},Validators.minLength=function(e){return function(t){if(s.isPresent(Validators.required(t)))return null;var n=t.value;return n.lengthe?{maxlength:{requiredLength:e,actualLength:n.length}}:null}},Validators.pattern=function(e){return function(t){if(s.isPresent(Validators.required(t)))return null;var n=new RegExp("^"+e+"$"),r=t.value;return n.test(r)?null:{pattern:{requiredPattern:"^"+e+"$",actualValue:r}}}},Validators.nullValidator=function(e){return null},Validators.compose=function(e){if(s.isBlank(e))return null;var t=e.filter(s.isPresent);return 0==t.length?null:function(e){return _mergeErrors(_executeValidators(e,t))}},Validators.composeAsync=function(e){if(s.isBlank(e))return null;var t=e.filter(s.isPresent);return 0==t.length?null:function(e){var n=_executeAsyncValidators(e,t).map(_convertToPromise);return Promise.all(n).then(_mergeErrors)}},Validators}();t.Validators=a},function(e,t,n){"use strict";var r=n(0),i=n(125),o=n(75),s=n(15),a=n(128),l=n(281);t.PRIMITIVE=String;var c=function(){function Serializer(e){this._renderStore=e}return Serializer.prototype.serialize=function(e,n){var i=this;if(!s.isPresent(e))return null;if(s.isArray(e))return e.map(function(e){return i.serialize(e,n)});if(n==t.PRIMITIVE)return e;if(n==u)return this._renderStore.serialize(e);if(n===r.RenderComponentType)return this._serializeRenderComponentType(e);if(n===r.ViewEncapsulation)return s.serializeEnum(e);if(n===l.LocationType)return this._serializeLocation(e);throw new o.BaseException("No serializer for "+n.toString())},Serializer.prototype.deserialize=function(e,n,a){var c=this;if(!s.isPresent(e))return null;if(s.isArray(e)){var p=[];return e.forEach(function(e){return p.push(c.deserialize(e,n,a))}),p}if(n==t.PRIMITIVE)return e;if(n==u)return this._renderStore.deserialize(e);if(n===r.RenderComponentType)return this._deserializeRenderComponentType(e);if(n===r.ViewEncapsulation)return i.VIEW_ENCAPSULATION_VALUES[e];if(n===l.LocationType)return this._deserializeLocation(e);throw new o.BaseException("No deserializer for "+n.toString())},Serializer.prototype._serializeLocation=function(e){return{href:e.href,protocol:e.protocol,host:e.host,hostname:e.hostname,port:e.port,pathname:e.pathname,search:e.search,hash:e.hash,origin:e.origin}},Serializer.prototype._deserializeLocation=function(e){return new l.LocationType(e.href,e.protocol,e.host,e.hostname,e.port,e.pathname,e.search,e.hash,e.origin)},Serializer.prototype._serializeRenderComponentType=function(e){return{id:e.id,templateUrl:e.templateUrl,slotCount:e.slotCount,encapsulation:this.serialize(e.encapsulation,r.ViewEncapsulation),styles:this.serialize(e.styles,t.PRIMITIVE)}},Serializer.prototype._deserializeRenderComponentType=function(e){return new r.RenderComponentType(e.id,e.templateUrl,e.slotCount,this.deserialize(e.encapsulation,r.ViewEncapsulation),this.deserialize(e.styles,t.PRIMITIVE),{})},Serializer.decorators=[{type:r.Injectable}],Serializer.ctorParameters=[{type:a.RenderStore}],Serializer}();t.Serializer=c;var u=function(){function RenderStoreObject(){}return RenderStoreObject}();t.RenderStoreObject=u},function(e,t,n){var r=n(2),i=n(25),o=n(14);e.exports=function(e,t){var n=(i.Object||{})[e]||Object[e],s={};s[e]=t(n),r(r.S+r.F*o(function(){n(1)}),"Object",s)}},function(e,t,n){"use strict";function _convertToPromise(e){return s.isPromise(e)?e:i.toPromise.call(e)}function _executeValidators(e,t){return t.map(function(t){return t(e)})}function _executeAsyncValidators(e,t){return t.map(function(t){return t(e)})}function _mergeErrors(e){var t=e.reduce(function(e,t){return s.isPresent(t)?o.StringMapWrapper.merge(e,t):e},{});return o.StringMapWrapper.isEmpty(t)?null:t}var r=n(0),i=n(314),o=n(34),s=n(7);t.NG_VALIDATORS=new r.OpaqueToken("NgValidators"),t.NG_ASYNC_VALIDATORS=new r.OpaqueToken("NgAsyncValidators");var a=function(){function Validators(){}return Validators.required=function(e){return s.isBlank(e.value)||s.isString(e.value)&&""==e.value?{required:!0}:null},Validators.minLength=function(e){return function(t){if(s.isPresent(Validators.required(t)))return null;var n=t.value;return n.lengthe?{maxlength:{requiredLength:e,actualLength:n.length}}:null}},Validators.pattern=function(e){return function(t){if(s.isPresent(Validators.required(t)))return null;var n=new RegExp("^"+e+"$"),r=t.value;return n.test(r)?null:{pattern:{requiredPattern:"^"+e+"$",actualValue:r}}}},Validators.nullValidator=function(e){return null},Validators.compose=function(e){if(s.isBlank(e))return null;var t=e.filter(s.isPresent);return 0==t.length?null:function(e){return _mergeErrors(_executeValidators(e,t))}},Validators.composeAsync=function(e){if(s.isBlank(e))return null;var t=e.filter(s.isPresent);return 0==t.length?null:function(e){var n=_executeAsyncValidators(e,t).map(_convertToPromise);return Promise.all(n).then(_mergeErrors)}},Validators}();t.Validators=a},function(e,t,n){"use strict";var r=this&&this.__extends||function(e,t){function __(){this.constructor=e}for(var n in t)t.hasOwnProperty(n)&&(e[n]=t[n]);e.prototype=null===t?Object.create(t):(__.prototype=t.prototype,new __)},i=n(86),o=n(7),s=function(e){function InvalidPipeArgumentException(t,n){e.call(this,"Invalid argument '"+n+"' for pipe '"+o.stringify(t)+"'")}return r(InvalidPipeArgumentException,e),InvalidPipeArgumentException}(i.BaseException);t.InvalidPipeArgumentException=s},function(e,t,n){"use strict";/** * @license * Copyright Google Inc. All Rights Reserved. * * Use of this source code is governed by an MIT-style license that can be * found in the LICENSE file at https://angular.io/license */ -var r=n(5),i=function(){function ParseLocation(e,t,n,r){this.file=e,this.offset=t,this.line=n,this.col=r}return ParseLocation.prototype.toString=function(){return r.isPresent(this.offset)?this.file.url+"@"+this.line+":"+this.col:this.file.url},ParseLocation}();t.ParseLocation=i;var o=function(){function ParseSourceFile(e,t){this.content=e,this.url=t}return ParseSourceFile}();t.ParseSourceFile=o;var s=function(){function ParseSourceSpan(e,t,n){void 0===n&&(n=null),this.start=e,this.end=t,this.details=n}return ParseSourceSpan.prototype.toString=function(){return this.start.file.content.substring(this.start.offset,this.end.offset)},ParseSourceSpan}();t.ParseSourceSpan=s,function(e){e[e.WARNING=0]="WARNING",e[e.FATAL=1]="FATAL"}(t.ParseErrorLevel||(t.ParseErrorLevel={}));var a=t.ParseErrorLevel,l=function(){function ParseError(e,t,n){void 0===n&&(n=a.FATAL),this.span=e,this.msg=t,this.level=n}return ParseError.prototype.toString=function(){var e=this.span.start.file.content,t=this.span.start.offset,n="",i="";if(r.isPresent(t)){t>e.length-1&&(t=e.length-1);for(var o=t,s=0,a=0;s<100&&t>0&&(t--,s++,"\n"!=e[t]||3!=++a););for(s=0,a=0;s<100&&o]"+e.substring(this.span.start.offset,o+1);n=' ("'+l+'")'}return this.span.details&&(i=", "+this.span.details),""+this.msg+n+": "+this.span.start+i},ParseError}();t.ParseError=l},function(e,t,n){"use strict";function templateVisitAll(e,t,n){void 0===n&&(n=null);var i=[];return t.forEach(function(t){var o=t.visit(e,n);r.isPresent(o)&&i.push(o)}),i}var r=n(5),i=function(){function TextAst(e,t,n){this.value=e,this.ngContentIndex=t,this.sourceSpan=n}return TextAst.prototype.visit=function(e,t){return e.visitText(this,t)},TextAst}();t.TextAst=i;var o=function(){function BoundTextAst(e,t,n){this.value=e,this.ngContentIndex=t,this.sourceSpan=n}return BoundTextAst.prototype.visit=function(e,t){return e.visitBoundText(this,t)},BoundTextAst}();t.BoundTextAst=o;var s=function(){function AttrAst(e,t,n){this.name=e,this.value=t,this.sourceSpan=n}return AttrAst.prototype.visit=function(e,t){return e.visitAttr(this,t)},AttrAst}();t.AttrAst=s;var a=function(){function BoundElementPropertyAst(e,t,n,r,i,o){this.name=e,this.type=t,this.securityContext=n,this.value=r,this.unit=i,this.sourceSpan=o}return BoundElementPropertyAst.prototype.visit=function(e,t){return e.visitElementProperty(this,t)},BoundElementPropertyAst}();t.BoundElementPropertyAst=a;var l=function(){function BoundEventAst(e,t,n,r){this.name=e,this.target=t,this.handler=n,this.sourceSpan=r}return BoundEventAst.prototype.visit=function(e,t){return e.visitEvent(this,t)},Object.defineProperty(BoundEventAst.prototype,"fullName",{get:function(){return r.isPresent(this.target)?this.target+":"+this.name:this.name},enumerable:!0,configurable:!0}),BoundEventAst}();t.BoundEventAst=l;var c=function(){function ReferenceAst(e,t,n){this.name=e,this.value=t,this.sourceSpan=n}return ReferenceAst.prototype.visit=function(e,t){return e.visitReference(this,t)},ReferenceAst}();t.ReferenceAst=c;var u=function(){function VariableAst(e,t,n){this.name=e,this.value=t,this.sourceSpan=n}return VariableAst.prototype.visit=function(e,t){return e.visitVariable(this,t)},VariableAst}();t.VariableAst=u;var p=function(){function ElementAst(e,t,n,r,i,o,s,a,l,c,u){this.name=e,this.attrs=t,this.inputs=n,this.outputs=r,this.references=i,this.directives=o,this.providers=s,this.hasViewContainer=a,this.children=l,this.ngContentIndex=c,this.sourceSpan=u}return ElementAst.prototype.visit=function(e,t){return e.visitElement(this,t)},ElementAst}();t.ElementAst=p;var d=function(){function EmbeddedTemplateAst(e,t,n,r,i,o,s,a,l,c){this.attrs=e,this.outputs=t,this.references=n,this.variables=r,this.directives=i,this.providers=o,this.hasViewContainer=s,this.children=a,this.ngContentIndex=l,this.sourceSpan=c}return EmbeddedTemplateAst.prototype.visit=function(e,t){return e.visitEmbeddedTemplate(this,t)},EmbeddedTemplateAst}();t.EmbeddedTemplateAst=d;var h=function(){function BoundDirectivePropertyAst(e,t,n,r){this.directiveName=e,this.templateName=t,this.value=n,this.sourceSpan=r}return BoundDirectivePropertyAst.prototype.visit=function(e,t){return e.visitDirectiveProperty(this,t)},BoundDirectivePropertyAst}();t.BoundDirectivePropertyAst=h;var f=function(){function DirectiveAst(e,t,n,r,i){this.directive=e,this.inputs=t,this.hostProperties=n,this.hostEvents=r,this.sourceSpan=i}return DirectiveAst.prototype.visit=function(e,t){return e.visitDirective(this,t)},DirectiveAst}();t.DirectiveAst=f;var m=function(){function ProviderAst(e,t,n,r,i,o,s){this.token=e,this.multiProvider=t,this.eager=n,this.providers=r,this.providerType=i,this.lifecycleHooks=o,this.sourceSpan=s}return ProviderAst.prototype.visit=function(e,t){return null},ProviderAst}();t.ProviderAst=m,function(e){e[e.PublicService=0]="PublicService",e[e.PrivateService=1]="PrivateService",e[e.Component=2]="Component",e[e.Directive=3]="Directive",e[e.Builtin=4]="Builtin"}(t.ProviderAstType||(t.ProviderAstType={}));var y=(t.ProviderAstType,function(){function NgContentAst(e,t,n){this.index=e,this.ngContentIndex=t,this.sourceSpan=n}return NgContentAst.prototype.visit=function(e,t){return e.visitNgContent(this,t)},NgContentAst}());t.NgContentAst=y,function(e){e[e.Property=0]="Property",e[e.Attribute=1]="Attribute",e[e.Class=2]="Class",e[e.Style=3]="Style",e[e.Animation=4]="Animation"}(t.PropertyBindingType||(t.PropertyBindingType={}));t.PropertyBindingType;t.templateVisitAll=templateVisitAll},function(e,t){"use strict";var n=function(){function MessageBus(){}return MessageBus}();t.MessageBus=n},function(e,t){"use strict";t.PRIMARY_OUTLET="primary"},function(e,t,n){var r=n(107),i=n(133),o=n(51),s=n(45),a=n(711);e.exports=function(e,t){var n=1==e,l=2==e,c=3==e,u=4==e,p=6==e,d=5==e||p,h=t||a;return function(t,a,f){for(var m,y,v=o(t),g=i(v),b=r(a,f,3),_=s(g.length),S=0,w=n?h(t,_):l?h(t,0):void 0;_>S;S++)if((d||S in g)&&(m=g[S],y=b(m,S,v),e))if(n)w[S]=y;else if(y)switch(e){case 3:return!0;case 5:return m;case 6:return S;case 2:w.push(m)}else if(u)return!1;return p?-1:c||u?u:w}}},function(e,t){e.exports=function(e){if(void 0==e)throw TypeError("Can't call method on "+e);return e}},function(e,t,n){var r=n(30),i=n(97);e.exports=n(35)?function(e,t,n){return r.f(e,t,i(1,n))}:function(e,t,n){return e[t]=n,e}},function(e,t,n){var r=n(475),i=n(2),o=n(197)("metadata"),s=o.store||(o.store=new(n(832))),a=function(e,t,n){var i=s.get(e);if(!i){if(!n)return;s.set(e,i=new r)}var o=i.get(t);if(!o){if(!n)return;i.set(t,o=new r)}return o},l=function(e,t,n){var r=a(t,n,!1);return void 0!==r&&r.has(e)},c=function(e,t,n){var r=a(t,n,!1);return void 0===r?void 0:r.get(e)},u=function(e,t,n,r){a(n,r,!0).set(e,t)},p=function(e,t){var n=a(e,t,!1),r=[];return n&&n.forEach(function(e,t){r.push(t)}),r},d=function(e){return void 0===e||"symbol"==typeof e?e:String(e)},h=function(e){i(i.S,"Reflect",e)};e.exports={store:s,map:a,has:l,get:c,set:u,keys:p,key:d,exp:h}},function(e,t,n){var r=n(39),i=n(51),o=n(301)("IE_PROTO"),s=Object.prototype;e.exports=Object.getPrototypeOf||function(e){return e=i(e),r(e,o)?e[o]:"function"==typeof e.constructor&&e instanceof e.constructor?e.constructor.prototype:e instanceof Object?s:null}},function(e,t){e.exports=".vt-row {\n display: flex;\n flex-wrap: wrap;\n height: 100%;\n width: 100%;\n}\n\n.vt-card {\n display: inline-table;\n margin-left: 25px;\n margin-bottom: 10px;\n margin-top: 10px;\n}\n\n.stats-container {\n width: 100%;\n}\n\n.vt-padding{\n padding-left: 25px;\n padding-right: 25px;\n}\n\n>>> p-dialog .ui-dialog{\n position: fixed !important;\n top: 50% !important;\n left: 50% !important;\n transform: translate(-50%, -50%);\n margin: 0;\n width: auto !important;\n}\n\n.vt-popUpContainer{\n position: fixed;\n padding: 0;\n margin: 0;\n z-index: 0;\n bottom: 0;\n right: 0;\n top: 0;\n left: 0;\n min-height: 1000vh;\n min-width: 1000vw;\n height: 100%;\n width: 100%;\n background: rgba(0,0,0,0.6);\n}\n\n.vt-dark-link:link {\n text-decoration: none;\n color: black;\n}\n\n.vt-dark-link:visited {\n text-decoration: none;\n color: black;\n}\n\n.vt-dark-link:hover {\n text-decoration: none;\n color: black;\n}\n\n.vt-dark-link:active {\n text-decoration: none;\n color: black;\n}\n\n/* Toolbar */\n.vt-toolbar {\n width: 100%;\n text-align: center;\n}\n\n>>> p-accordiontab a {\n padding-left: 25px! important;\n}\n\n>>> .ui-accordion-content button {\n margin-top: 2px;\n}\n\n>>> p-menu .ui-menu {\n margin-top: 19px;\n display: inline-block;\n top: auto !important;\n left: auto !important;\n float: right;\n \n}\n\np-menu {\n display: inline-block;\n float: left;\n}\n\n.vt-toolbar .vt-menu {\n padding-top: 19px;\n float: left;\n}\n\n.vt-toolbar .vt-right-menu {\n padding-top: 19px;\n position: fixed;\n right: 25px;\n top: 19px;\n}\n\n.vt-card-toolbar {\n display: inline-block;\n width: 100%;\n}\n\n.vt-card-toolbar .vt-menu {\n float: left;\n}\n.vt-card-toolbar .vt-title {\n float: right;\n margin: 0;\n padding-left: 25px;\n}\n\nmd-list:hover {\n background: #E8E8E8\n}\n"},function(e,t){var n;n=function(){return this}();try{n=n||Function("return this")()||(0,eval)("this")}catch(r){"object"==typeof window&&(n=window)}e.exports=n},function(e,t,n){"use strict";var r=n(366),i=function(){function InterpolationConfig(e,t){this.start=e,this.end=t}return InterpolationConfig.fromArray=function(e){return e?(r.assertInterpolationSymbols("interpolation",e),new InterpolationConfig(e[0],e[1])):t.DEFAULT_INTERPOLATION_CONFIG},InterpolationConfig}();t.InterpolationConfig=i,t.DEFAULT_INTERPOLATION_CONFIG=new i("{{","}}")},[1097,266],function(e,t,n){"use strict";function controlPath(e,t){var n=r.ListWrapper.clone(t.path);return n.push(e),n}function setUpControl(e,t){o.isBlank(e)&&_throwError(t,"Cannot find control with"),o.isBlank(t.valueAccessor)&&_throwError(t,"No value accessor for form control with"),e.validator=s.Validators.compose([e.validator,t.validator]),e.asyncValidator=s.Validators.composeAsync([e.asyncValidator,t.asyncValidator]),t.valueAccessor.writeValue(e.value),t.valueAccessor.registerOnChange(function(n){t.viewToModelUpdate(n),e.markAsDirty(),e.setValue(n,{emitModelToViewChange:!1})}),e.registerOnChange(function(e,n){t.valueAccessor.writeValue(e),n&&t.viewToModelUpdate(e)}),t.valueAccessor.registerOnTouched(function(){return e.markAsTouched()})}function setUpFormContainer(e,t){o.isBlank(e)&&_throwError(t,"Cannot find control with"),e.validator=s.Validators.compose([e.validator,t.validator]),e.asyncValidator=s.Validators.composeAsync([e.asyncValidator,t.asyncValidator])}function _throwError(e,t){var n;throw n=e.path.length>1?"path: '"+e.path.join(" -> ")+"'":e.path[0]?"name: '"+e.path+"'":"unspecified name attribute",new i.BaseException(t+" "+n)}function composeValidators(e){return o.isPresent(e)?s.Validators.compose(e.map(c.normalizeValidator)):null}function composeAsyncValidators(e){return o.isPresent(e)?s.Validators.composeAsync(e.map(c.normalizeAsyncValidator)):null}function isPropertyUpdated(e,t){if(!r.StringMapWrapper.contains(e,"model"))return!1;var n=e.model;return!!n.isFirstChange()||!o.looseIdentical(t,n.currentValue)}function selectValueAccessor(e,t){if(o.isBlank(t))return null;var n,r,i;return t.forEach(function(t){o.hasConstructor(t,l.DefaultValueAccessor)?n=t:o.hasConstructor(t,a.CheckboxControlValueAccessor)||o.hasConstructor(t,u.NumberValueAccessor)||o.hasConstructor(t,d.SelectControlValueAccessor)||o.hasConstructor(t,h.SelectMultipleControlValueAccessor)||o.hasConstructor(t,p.RadioControlValueAccessor)?(o.isPresent(r)&&_throwError(e,"More than one built-in value accessor matches form control with"),r=t):(o.isPresent(i)&&_throwError(e,"More than one custom value accessor matches form control with"),i=t)}),o.isPresent(i)?i:o.isPresent(r)?r:o.isPresent(n)?n:(_throwError(e,"No valid value accessor for form control with"),null)}var r=n(48),i=n(91),o=n(32),s=n(55),a=n(172),l=n(173),c=n(635),u=n(269),p=n(175),d=n(176),h=n(177);t.controlPath=controlPath,t.setUpControl=setUpControl,t.setUpFormContainer=setUpFormContainer,t.composeValidators=composeValidators,t.composeAsyncValidators=composeAsyncValidators,t.isPropertyUpdated=isPropertyUpdated,t.selectValueAccessor=selectValueAccessor},function(e,t){"use strict";!function(e){e[e.Get=0]="Get",e[e.Post=1]="Post",e[e.Put=2]="Put",e[e.Delete=3]="Delete",e[e.Options=4]="Options",e[e.Head=5]="Head",e[e.Patch=6]="Patch"}(t.RequestMethod||(t.RequestMethod={}));t.RequestMethod;!function(e){e[e.Unsent=0]="Unsent",e[e.Open=1]="Open",e[e.HeadersReceived=2]="HeadersReceived",e[e.Loading=3]="Loading",e[e.Done=4]="Done",e[e.Cancelled=5]="Cancelled"}(t.ReadyState||(t.ReadyState={}));t.ReadyState;!function(e){e[e.Basic=0]="Basic",e[e.Cors=1]="Cors",e[e.Default=2]="Default",e[e.Error=3]="Error",e[e.Opaque=4]="Opaque"}(t.ResponseType||(t.ResponseType={}));t.ResponseType;!function(e){e[e.NONE=0]="NONE",e[e.JSON=1]="JSON",e[e.FORM=2]="FORM",e[e.FORM_DATA=3]="FORM_DATA",e[e.TEXT=4]="TEXT",e[e.BLOB=5]="BLOB",e[e.ARRAY_BUFFER=6]="ARRAY_BUFFER"}(t.ContentType||(t.ContentType={}));t.ContentType;!function(e){e[e.Text=0]="Text",e[e.Json=1]="Json",e[e.ArrayBuffer=2]="ArrayBuffer",e[e.Blob=3]="Blob"}(t.ResponseContentType||(t.ResponseContentType={}));t.ResponseContentType},[1096,437,438,438],function(e,t,n){"use strict";function createEmptyUrlTree(){return new o(new s([],{}),{},null)}function containsTree(e,t,n){return n?equalSegmentGroups(e.root,t.root):containsSegmentGroup(e.root,t.root)}function equalSegmentGroups(e,t){if(!equalPath(e.segments,t.segments))return!1;if(e.numberOfChildren!==t.numberOfChildren)return!1;for(var n in t.children){if(!e.children[n])return!1;if(!equalSegmentGroups(e.children[n],t.children[n]))return!1}return!0}function containsSegmentGroup(e,t){return containsSegmentGroupHelper(e,t,t.segments)}function containsSegmentGroupHelper(e,t,n){if(e.segments.length>n.length){var i=e.segments.slice(0,n.length);return!!equalPath(i,n)&&!t.hasChildren()}if(e.segments.length===n.length){if(!equalPath(e.segments,n))return!1;for(var o in t.children){if(!e.children[o])return!1;if(!containsSegmentGroup(e.children[o],t.children[o]))return!1}return!0}var i=n.slice(0,e.segments.length),s=n.slice(e.segments.length);return!!equalPath(e.segments,i)&&(!!e.children[r.PRIMARY_OUTLET]&&containsSegmentGroupHelper(e.children[r.PRIMARY_OUTLET],t,s))}function equalSegments(e,t){if(e.length!==t.length)return!1;for(var n=0;n0?n+"("+o.join("//")+")":""+n}if(e.hasChildren()&&!t){var s=mapChildrenIntoArray(e,function(t,n){return n===r.PRIMARY_OUTLET?[serializeSegment(e.children[r.PRIMARY_OUTLET],!1)]:[n+":"+serializeSegment(t,!1)]});return serializePaths(e)+"/("+s.join("//")+")"}return serializePaths(e)}function encode(e){return encodeURIComponent(e)}function decode(e){return decodeURIComponent(e)}function serializePath(e){return""+encode(e.path)+serializeParams(e.parameters)}function serializeParams(e){return pairs(e).map(function(e){return";"+encode(e.first)+"="+encode(e.second)}).join("")}function serializeQueryParams(e){var t=pairs(e).map(function(e){return encode(e.first)+"="+encode(e.second)});return t.length>0?"?"+t.join("&"):""}function pairs(e){var t=[];for(var n in e)e.hasOwnProperty(n)&&t.push(new u(n,e[n]));return t}function matchSegments(e){p.lastIndex=0;var t=e.match(p);return t?t[0]:""}function matchQueryParams(e){d.lastIndex=0;var t=e.match(p);return t?t[0]:""}function matchUrlQueryParamValue(e){h.lastIndex=0;var t=e.match(h);return t?t[0]:""}var r=n(63),i=n(77);t.createEmptyUrlTree=createEmptyUrlTree,t.containsTree=containsTree;var o=function(){function UrlTree(e,t,n){this.root=e,this.queryParams=t,this.fragment=n}return UrlTree.prototype.toString=function(){return(new c).serialize(this)},UrlTree}();t.UrlTree=o;var s=function(){function UrlSegmentGroup(e,t){var n=this;this.segments=e,this.children=t,this.parent=null,i.forEach(t,function(e,t){return e.parent=n})}return UrlSegmentGroup.prototype.hasChildren=function(){return this.numberOfChildren>0},Object.defineProperty(UrlSegmentGroup.prototype,"numberOfChildren",{get:function(){return Object.keys(this.children).length},enumerable:!0,configurable:!0}),UrlSegmentGroup.prototype.toString=function(){return serializePaths(this)},UrlSegmentGroup}();t.UrlSegmentGroup=s;var a=function(){function UrlSegment(e,t){this.path=e,this.parameters=t}return UrlSegment.prototype.toString=function(){return serializePath(this)},UrlSegment}();t.UrlSegment=a,t.equalSegments=equalSegments,t.equalPath=equalPath,t.mapChildrenIntoArray=mapChildrenIntoArray;var l=function(){function UrlSerializer(){}return UrlSerializer}();t.UrlSerializer=l;var c=function(){function DefaultUrlSerializer(){}return DefaultUrlSerializer.prototype.parse=function(e){var t=new f(e);return new o(t.parseRootSegment(),t.parseQueryParams(),t.parseFragment())},DefaultUrlSerializer.prototype.serialize=function(e){var t="/"+serializeSegment(e.root,!0),n=serializeQueryParams(e.queryParams),r=null!==e.fragment&&void 0!==e.fragment?"#"+encodeURIComponent(e.fragment):"";return""+t+n+r},DefaultUrlSerializer}();t.DefaultUrlSerializer=c,t.serializePaths=serializePaths,t.encode=encode,t.decode=decode,t.serializePath=serializePath;var u=function(){function Pair(e,t){this.first=e,this.second=t}return Pair}(),p=/^[^\/\(\)\?;=&#]+/,d=/^[^=\?&#]+/,h=/^[^\?&#]+/,f=function(){function UrlParser(e){this.url=e,this.remaining=e}return UrlParser.prototype.peekStartsWith=function(e){return this.remaining.startsWith(e)},UrlParser.prototype.capture=function(e){if(!this.remaining.startsWith(e))throw new Error('Expected "'+e+'".');this.remaining=this.remaining.substring(e.length)},UrlParser.prototype.parseRootSegment=function(){return this.remaining.startsWith("/")&&this.capture("/"),""===this.remaining||this.remaining.startsWith("?")||this.remaining.startsWith("#")?new s([],{}):new s([],this.parseChildren())},UrlParser.prototype.parseChildren=function(){if(0==this.remaining.length)return{};this.peekStartsWith("/")&&this.capture("/");var e=[];for(this.peekStartsWith("(")||e.push(this.parseSegments());this.peekStartsWith("/")&&!this.peekStartsWith("//")&&!this.peekStartsWith("/(");)this.capture("/"),e.push(this.parseSegments());var t={};this.peekStartsWith("/(")&&(this.capture("/"),t=this.parseParens(!0));var n={};return this.peekStartsWith("(")&&(n=this.parseParens(!1)),(e.length>0||Object.keys(t).length>0)&&(n[r.PRIMARY_OUTLET]=new s(e,t)),n},UrlParser.prototype.parseSegments=function(){var e=matchSegments(this.remaining);if(""===e&&this.peekStartsWith(";"))throw new Error("Empty path url segment cannot have parameters: '"+this.remaining+"'.");this.capture(e);var t={};return this.peekStartsWith(";")&&(t=this.parseMatrixParams()),new a(decode(e),t)},UrlParser.prototype.parseQueryParams=function(){var e={};if(this.peekStartsWith("?"))for(this.capture("?"),this.parseQueryParam(e);this.remaining.length>0&&this.peekStartsWith("&");)this.capture("&"),this.parseQueryParam(e);return e},UrlParser.prototype.parseFragment=function(){return this.peekStartsWith("#")?decode(this.remaining.substring(1)):null},UrlParser.prototype.parseMatrixParams=function(){for(var e={};this.remaining.length>0&&this.peekStartsWith(";");)this.capture(";"),this.parseParam(e);return e},UrlParser.prototype.parseParam=function(e){var t=matchSegments(this.remaining);if(t){this.capture(t);var n="true";if(this.peekStartsWith("=")){this.capture("=");var r=matchSegments(this.remaining);r&&(n=r,this.capture(n))}e[decode(t)]=decode(n)}},UrlParser.prototype.parseQueryParam=function(e){var t=matchQueryParams(this.remaining);if(t){this.capture(t);var n="";if(this.peekStartsWith("=")){this.capture("=");var r=matchUrlQueryParamValue(this.remaining);r&&(n=r,this.capture(n))}e[decode(t)]=decode(n)}},UrlParser.prototype.parseParens=function(e){var t={};for(this.capture("(");!this.peekStartsWith(")")&&this.remaining.length>0;){var n=matchSegments(this.remaining),i=this.remaining[n.length];if("/"!==i&&")"!==i&&";"!==i)throw new Error("Cannot parse url '"+this.url+"'");var o=void 0;n.indexOf(":")>-1?(o=n.substr(0,n.indexOf(":")),this.capture(o),this.capture(":")):e&&(o=r.PRIMARY_OUTLET);var a=this.parseChildren();t[o]=1===Object.keys(a).length?a[r.PRIMARY_OUTLET]:new s([],a),this.peekStartsWith("//")&&this.capture("//")}return this.capture(")"),t},UrlParser}()},function(e,t,n){"use strict";function shallowEqualArrays(e,t){if(e.length!==t.length)return!1;for(var n=0;n0?e[0]:null}function last(e){return e.length>0?e[e.length-1]:null}function and(e){return e.reduce(function(e,t){return e&&t},!0)}function merge(e,t){var n={};for(var r in e)e.hasOwnProperty(r)&&(n[r]=e[r]);for(var r in t)t.hasOwnProperty(r)&&(n[r]=t[r]);return n}function forEach(e,t){for(var n in e)e.hasOwnProperty(n)&&t(e[n],n)}function waitForMap(e,t){var n=[],r={};return forEach(e,function(e,i){i===s.PRIMARY_OUTLET&&n.push(t(i,e).map(function(e){return r[i]=e,e}))}),forEach(e,function(e,i){i!==s.PRIMARY_OUTLET&&n.push(t(i,e).map(function(e){return r[i]=e,e}))}),n.length>0?o.of.apply(void 0,n).concatAll().last().map(function(e){return r}):o.of(r)}function andObservables(e){return e.mergeAll().every(function(e){return e===!0})}function wrapIntoObservable(e){return e instanceof r.Observable?e:e instanceof Promise?i.fromPromise(e):o.of(e)}n(305),n(498);var r=n(1),i=n(208),o=n(140),s=n(63);t.shallowEqualArrays=shallowEqualArrays,t.shallowEqual=shallowEqual,t.flatten=flatten,t.first=first,t.last=last,t.and=and,t.merge=merge,t.forEach=forEach,t.waitForMap=waitForMap,t.andObservables=andObservables,t.wrapIntoObservable=wrapIntoObservable},function(e,t,n){var r=n(137)("meta"),i=n(17),o=n(39),s=n(30).f,a=0,l=Object.isExtensible||function(){return!0},c=!n(14)(function(){return l(Object.preventExtensions({}))}),u=function(e){s(e,r,{value:{i:"O"+ ++a,w:{}}})},p=function(e,t){if(!i(e))return"symbol"==typeof e?e:("string"==typeof e?"S":"P")+e;if(!o(e,r)){if(!l(e))return"F";if(!t)return"E";u(e)}return e[r].i},d=function(e,t){if(!o(e,r)){if(!l(e))return!0;if(!t)return!1;u(e)}return e[r].w},h=function(e){return c&&f.NEED&&l(e)&&!o(e,r)&&u(e),e},f=e.exports={KEY:r,NEED:!1,fastKey:p,getWeak:d,onFreeze:h}},function(e,t,n){var r=n(195),i=n(97),o=n(50),s=n(98),a=n(39),l=n(456),c=Object.getOwnPropertyDescriptor;t.f=n(35)?c:function(e,t){if(e=o(e),t=s(t,!0),l)try{return c(e,t)}catch(n){}if(a(e,t))return i(!r.f.call(e,t),e[t])}},function(e,t,n){"use strict";var r=this&&this.__extends||function(e,t){function __(){this.constructor=e}for(var n in t)t.hasOwnProperty(n)&&(e[n]=t[n]);e.prototype=null===t?Object.create(t):(__.prototype=t.prototype,new __)},i=n(1),o=n(309),s=n(81),a=n(99),l=function(e){function ArrayObservable(t,n){e.call(this),this.array=t,this.scheduler=n,n||1!==t.length||(this._isScalar=!0,this.value=t[0])}return r(ArrayObservable,e),ArrayObservable.create=function(e,t){return new ArrayObservable(e,t)},ArrayObservable.of=function(){for(var e=[],t=0;t1?new ArrayObservable(e,n):1===r?new o.ScalarObservable(e[0],n):new s.EmptyObservable(n)},ArrayObservable.dispatch=function(e){var t=e.array,n=e.index,r=e.count,i=e.subscriber;return n>=r?void i.complete():(i.next(t[n]),void(i.isUnsubscribed||(e.index=n+1,this.schedule(e))))},ArrayObservable.prototype._subscribe=function(e){var t=0,n=this.array,r=n.length,i=this.scheduler;if(i)return i.schedule(ArrayObservable.dispatch,0,{array:n,index:t,count:r,subscriber:e});for(var o=0;o=0;a--)(i=e[a])&&(s=(o<3?i(s):o>3?i(t,n,s):i(t,n))||s);return o>3&&s&&Object.defineProperty(t,n,s),s},s=this&&this.__metadata||function(e,t){if("object"==typeof Reflect&&"function"==typeof Reflect.metadata)return Reflect.metadata(e,t)},a=function(){function VtctlService(e){this.http=e,this.vtctlUrl="../api/vtctl/"}return VtctlService.prototype.sendPostRequest=function(e,t){var n=new r.Headers({"Content-Type":"application/json"}),i=new r.RequestOptions({headers:n});return this.http.post(e,JSON.stringify(t),i).map(function(e){return e.json()})},VtctlService.prototype.runCommand=function(e){return this.sendPostRequest(this.vtctlUrl,e)},VtctlService=o([n.i(i.Injectable)(),s("design:paramtypes",["function"==typeof(e="undefined"!=typeof r.Http&&r.Http)&&e||Object])],VtctlService);var e}()},function(e,t,n){"use strict";var r=n(146);n.d(t,"a",function(){return i});var i=function(){function DialogContent(e,t,n,r,i){void 0===e&&(e=""),void 0===t&&(t={}),void 0===n&&(n={}),void 0===r&&(r=void 0),void 0===i&&(i=""),this.nameId=e,this.flags=t,this.requiredFlags=n,this.prepareFunction=r,this.action=i}return DialogContent.prototype.getName=function(){return this.flags[this.nameId]?this.flags[this.nameId].getStrValue():""},DialogContent.prototype.setName=function(e){this.flags[this.nameId]&&this.flags[this.nameId].setValue(e)},DialogContent.prototype.getPostBody=function(e){void 0===e&&(e=void 0),e||(e=this.getFlags());var t=[],n=[];t.push(this.action);for(var r=0,i=e;r0?" { "+e.children.map(serializeNode).join(", ")+" } ":"";return""+e.value+t}function advanceActivatedRoute(e){e.snapshot?(a.shallowEqual(e.snapshot.queryParams,e._futureSnapshot.queryParams)||e.queryParams.next(e._futureSnapshot.queryParams),e.snapshot.fragment!==e._futureSnapshot.fragment&&e.fragment.next(e._futureSnapshot.fragment),a.shallowEqual(e.snapshot.params,e._futureSnapshot.params)||(e.params.next(e._futureSnapshot.params),e.data.next(e._futureSnapshot.data)),a.shallowEqualArrays(e.snapshot.url,e._futureSnapshot.url)||e.url.next(e._futureSnapshot.url),e.snapshot=e._futureSnapshot):(e.snapshot=e._futureSnapshot,e.data.next(e._futureSnapshot.data))}var r=this&&this.__extends||function(e,t){function __(){this.constructor=e}for(var n in t)t.hasOwnProperty(n)&&(e[n]=t[n]);e.prototype=null===t?Object.create(t):(__.prototype=t.prototype,new __)},i=n(204),o=n(63),s=n(76),a=n(77),l=n(284),c=function(e){function RouterState(t,n){e.call(this,t),this.snapshot=n,setRouterStateSnapshot(this,t)}return r(RouterState,e),Object.defineProperty(RouterState.prototype,"queryParams",{get:function(){return this.root.queryParams},enumerable:!0,configurable:!0}),Object.defineProperty(RouterState.prototype,"fragment",{get:function(){return this.root.fragment},enumerable:!0,configurable:!0}),RouterState.prototype.toString=function(){return this.snapshot.toString()},RouterState}(l.Tree);t.RouterState=c,t.createEmptyState=createEmptyState;var u=function(){function ActivatedRoute(e,t,n,r,i,o,s,a){this.url=e,this.params=t,this.queryParams=n,this.fragment=r,this.data=i,this.outlet=o,this.component=s,this._futureSnapshot=a}return Object.defineProperty(ActivatedRoute.prototype,"routeConfig",{get:function(){return this._futureSnapshot.routeConfig},enumerable:!0,configurable:!0}),Object.defineProperty(ActivatedRoute.prototype,"root",{get:function(){return this._routerState.root},enumerable:!0,configurable:!0}),Object.defineProperty(ActivatedRoute.prototype,"parent",{get:function(){return this._routerState.parent(this)},enumerable:!0,configurable:!0}),Object.defineProperty(ActivatedRoute.prototype,"firstChild",{get:function(){return this._routerState.firstChild(this)},enumerable:!0,configurable:!0}),Object.defineProperty(ActivatedRoute.prototype,"children",{get:function(){return this._routerState.children(this)},enumerable:!0,configurable:!0}),Object.defineProperty(ActivatedRoute.prototype,"pathFromRoot",{get:function(){return this._routerState.pathFromRoot(this)},enumerable:!0,configurable:!0}),ActivatedRoute.prototype.toString=function(){return this.snapshot?this.snapshot.toString():"Future("+this._futureSnapshot+")"},ActivatedRoute}();t.ActivatedRoute=u;var p=function(){function InheritedResolve(e,t){this.parent=e,this.current=t,this.resolvedData={}}return Object.defineProperty(InheritedResolve.prototype,"flattenedResolvedData",{get:function(){return this.parent?a.merge(this.parent.flattenedResolvedData,this.resolvedData):this.resolvedData},enumerable:!0,configurable:!0}),Object.defineProperty(InheritedResolve,"empty",{get:function(){return new InheritedResolve(null,{})},enumerable:!0,configurable:!0}),InheritedResolve}();t.InheritedResolve=p;var d=function(){function ActivatedRouteSnapshot(e,t,n,r,i,o,s,a,l,c,u){this.url=e,this.params=t,this.queryParams=n,this.fragment=r,this.data=i,this.outlet=o,this.component=s,this._routeConfig=a,this._urlSegment=l,this._lastPathIndex=c,this._resolve=u}return Object.defineProperty(ActivatedRouteSnapshot.prototype,"routeConfig",{get:function(){return this._routeConfig},enumerable:!0,configurable:!0}),Object.defineProperty(ActivatedRouteSnapshot.prototype,"root",{get:function(){return this._routerState.root},enumerable:!0,configurable:!0}),Object.defineProperty(ActivatedRouteSnapshot.prototype,"parent",{get:function(){return this._routerState.parent(this)},enumerable:!0,configurable:!0}),Object.defineProperty(ActivatedRouteSnapshot.prototype,"firstChild",{get:function(){return this._routerState.firstChild(this)},enumerable:!0,configurable:!0}),Object.defineProperty(ActivatedRouteSnapshot.prototype,"children",{get:function(){return this._routerState.children(this)},enumerable:!0,configurable:!0}),Object.defineProperty(ActivatedRouteSnapshot.prototype,"pathFromRoot",{get:function(){return this._routerState.pathFromRoot(this)},enumerable:!0,configurable:!0}),ActivatedRouteSnapshot.prototype.toString=function(){var e=this.url.map(function(e){return e.toString()}).join("/"),t=this._routeConfig?this._routeConfig.path:"";return"Route(url:'"+e+"', path:'"+t+"')"},ActivatedRouteSnapshot}();t.ActivatedRouteSnapshot=d;var h=function(e){function RouterStateSnapshot(t,n){e.call(this,n),this.url=t,setRouterStateSnapshot(this,n)}return r(RouterStateSnapshot,e),Object.defineProperty(RouterStateSnapshot.prototype,"queryParams",{get:function(){return this.root.queryParams},enumerable:!0,configurable:!0}),Object.defineProperty(RouterStateSnapshot.prototype,"fragment",{get:function(){return this.root.fragment},enumerable:!0,configurable:!0}),RouterStateSnapshot.prototype.toString=function(){return serializeNode(this._root)},RouterStateSnapshot}(l.Tree);t.RouterStateSnapshot=h,t.advanceActivatedRoute=advanceActivatedRoute},function(e,t){e.exports=function(e){if("function"!=typeof e)throw TypeError(e+" is not a function!");return e}},function(e,t){var n={}.toString;e.exports=function(e){return n.call(e).slice(8,-1)}},function(e,t){e.exports=function(e,t){return{enumerable:!(1&e),configurable:!(2&e),writable:!(4&e),value:t}}},function(e,t,n){var r=n(17);e.exports=function(e,t){if(!r(e))return e;var n,i;if(t&&"function"==typeof(n=e.toString)&&!r(i=n.call(e)))return i;if("function"==typeof(n=e.valueOf)&&!r(i=n.call(e)))return i;if(!t&&"function"==typeof(n=e.toString)&&!r(i=n.call(e)))return i;throw TypeError("Can't convert object to primitive value")}},function(e,t){"use strict";function isScheduler(e){return e&&"function"==typeof e.schedule}t.isScheduler=isScheduler},function(e,t,n){"use strict";var r=this&&this.__extends||function(e,t){function __(){this.constructor=e}for(var n in t)t.hasOwnProperty(n)&&(e[n]=t[n]);e.prototype=null===t?Object.create(t):(__.prototype=t.prototype,new __)},i=n(20),o=n(1);t.Observable=o.Observable;var s=n(20);t.Subject=s.Subject;var a=function(e){function EventEmitter(t){void 0===t&&(t=!1),e.call(this),this.__isAsync=t}return r(EventEmitter,e),EventEmitter.prototype.emit=function(t){e.prototype.next.call(this,t)},EventEmitter.prototype.next=function(t){e.prototype.next.call(this,t)},EventEmitter.prototype.subscribe=function(t,n,r){var i,o=function(e){return null},s=function(){return null};return t&&"object"==typeof t?(i=this.__isAsync?function(e){setTimeout(function(){return t.next(e)})}:function(e){t.next(e)},t.error&&(o=this.__isAsync?function(e){setTimeout(function(){return t.error(e)})}:function(e){t.error(e)}),t.complete&&(s=this.__isAsync?function(){setTimeout(function(){return t.complete()})}:function(){t.complete()})):(i=this.__isAsync?function(e){setTimeout(function(){return t(e)})}:function(e){t(e)},n&&(o=this.__isAsync?function(e){setTimeout(function(){return n(e)})}:function(e){n(e)}),r&&(s=this.__isAsync?function(){setTimeout(function(){return r()})}:function(){r()})),e.prototype.subscribe.call(this,i,o,s)},EventEmitter}(i.Subject);t.EventEmitter=a},function(e,t,n){"use strict";function controlPath(e,t){var n=r.ListWrapper.clone(t.path);return n.push(e),n}function setUpControl(e,t){o.isBlank(e)&&_throwError(t,"Cannot find control with"),o.isBlank(t.valueAccessor)&&_throwError(t,"No value accessor for form control with"),e.validator=s.Validators.compose([e.validator,t.validator]),e.asyncValidator=s.Validators.composeAsync([e.asyncValidator,t.asyncValidator]),t.valueAccessor.writeValue(e.value),t.valueAccessor.registerOnChange(function(n){t.viewToModelUpdate(n),e.updateValue(n,{emitModelToViewChange:!1}),e.markAsDirty()}),e.registerOnChange(function(e){return t.valueAccessor.writeValue(e)}),t.valueAccessor.registerOnTouched(function(){return e.markAsTouched()})}function setUpControlGroup(e,t){o.isBlank(e)&&_throwError(t,"Cannot find control with"),e.validator=s.Validators.compose([e.validator,t.validator]),e.asyncValidator=s.Validators.composeAsync([e.asyncValidator,t.asyncValidator])}function _throwError(e,t){var n;throw n=e.path.length>1?"path: '"+e.path.join(" -> ")+"'":e.path[0]?"name: '"+e.path+"'":"unspecified name",new i.BaseException(t+" "+n)}function composeValidators(e){return o.isPresent(e)?s.Validators.compose(e.map(c.normalizeValidator)):null}function composeAsyncValidators(e){return o.isPresent(e)?s.Validators.composeAsync(e.map(c.normalizeAsyncValidator)):null}function isPropertyUpdated(e,t){if(!r.StringMapWrapper.contains(e,"model"))return!1;var n=e.model;return!!n.isFirstChange()||!o.looseIdentical(t,n.currentValue)}function selectValueAccessor(e,t){if(o.isBlank(t))return null;var n,r,i;return t.forEach(function(t){o.hasConstructor(t,l.DefaultValueAccessor)?n=t:o.hasConstructor(t,a.CheckboxControlValueAccessor)||o.hasConstructor(t,u.NumberValueAccessor)||o.hasConstructor(t,d.SelectControlValueAccessor)||o.hasConstructor(t,h.SelectMultipleControlValueAccessor)||o.hasConstructor(t,p.RadioControlValueAccessor)?(o.isPresent(r)&&_throwError(e,"More than one built-in value accessor matches form control with"),r=t):(o.isPresent(i)&&_throwError(e,"More than one custom value accessor matches form control with"),i=t)}),o.isPresent(i)?i:o.isPresent(r)?r:o.isPresent(n)?n:(_throwError(e,"No valid value accessor for form control with"),null)}var r=n(34),i=n(86),o=n(7),s=n(58),a=n(147),l=n(148),c=n(574),u=n(230),p=n(149),d=n(150),h=n(231);t.controlPath=controlPath,t.setUpControl=setUpControl,t.setUpControlGroup=setUpControlGroup,t.composeValidators=composeValidators,t.composeAsyncValidators=composeAsyncValidators,t.isPropertyUpdated=isPropertyUpdated,t.selectValueAccessor=selectValueAccessor},function(e,t,n){"use strict";var r=n(0),i=n(18),o=n(28),s=function(){function CompilerConfig(e){var t=void 0===e?{}:e,n=t.renderTypes,i=void 0===n?new l:n,o=t.defaultEncapsulation,s=void 0===o?r.ViewEncapsulation.Emulated:o,a=t.genDebugInfo,c=t.logBindingUpdate,u=t.useJit,p=void 0===u||u,d=t.deprecatedPlatformDirectives,h=void 0===d?[]:d,f=t.deprecatedPlatformPipes,m=void 0===f?[]:f;this.renderTypes=i,this.defaultEncapsulation=s,this._genDebugInfo=a,this._logBindingUpdate=c,this.useJit=p,this.platformDirectives=h,this.platformPipes=m}return Object.defineProperty(CompilerConfig.prototype,"genDebugInfo",{get:function(){return void 0===this._genDebugInfo?r.isDevMode():this._genDebugInfo},enumerable:!0,configurable:!0}),Object.defineProperty(CompilerConfig.prototype,"logBindingUpdate",{get:function(){return void 0===this._logBindingUpdate?r.isDevMode():this._logBindingUpdate},enumerable:!0,configurable:!0}),CompilerConfig}();t.CompilerConfig=s;var a=function(){function RenderTypes(){}return Object.defineProperty(RenderTypes.prototype,"renderer",{get:function(){return i.unimplemented()},enumerable:!0,configurable:!0}),Object.defineProperty(RenderTypes.prototype,"renderText",{get:function(){return i.unimplemented()},enumerable:!0,configurable:!0}),Object.defineProperty(RenderTypes.prototype,"renderElement",{get:function(){return i.unimplemented()},enumerable:!0,configurable:!0}),Object.defineProperty(RenderTypes.prototype,"renderComment",{get:function(){return i.unimplemented()},enumerable:!0,configurable:!0}),Object.defineProperty(RenderTypes.prototype,"renderNode",{get:function(){return i.unimplemented()},enumerable:!0,configurable:!0}),Object.defineProperty(RenderTypes.prototype,"renderEvent",{get:function(){return i.unimplemented()},enumerable:!0,configurable:!0}),RenderTypes}();t.RenderTypes=a;var l=function(){function DefaultRenderTypes(){this.renderer=o.Identifiers.Renderer,this.renderText=null,this.renderElement=null,this.renderComment=null,this.renderNode=null,this.renderEvent=null}return DefaultRenderTypes}();t.DefaultRenderTypes=l},function(e,t){"use strict";function splitNsName(e){if(":"!=e[0])return[null,e];var t=e.indexOf(":",1);if(t==-1)throw new Error('Unsupported format "'+e+'" expecting ":namespace:name"');return[e.slice(1,t),e.slice(t+1)]}function getNsPrefix(e){return null===e?null:splitNsName(e)[0]}function mergeNsAndName(e,t){return e?":"+e+":"+t:t}!function(e){e[e.RAW_TEXT=0]="RAW_TEXT",e[e.ESCAPABLE_RAW_TEXT=1]="ESCAPABLE_RAW_TEXT",e[e.PARSABLE_DATA=2]="PARSABLE_DATA"}(t.TagContentType||(t.TagContentType={}));t.TagContentType;t.splitNsName=splitNsName,t.getNsPrefix=getNsPrefix,t.mergeNsAndName=mergeNsAndName,t.NAMED_ENTITIES={Aacute:"Á",aacute:"á",Acirc:"Â",acirc:"â",acute:"´",AElig:"Æ",aelig:"æ",Agrave:"À",agrave:"à",alefsym:"ℵ",Alpha:"Α",alpha:"α",amp:"&",and:"∧",ang:"∠",apos:"'",Aring:"Å",aring:"å",asymp:"≈",Atilde:"Ã",atilde:"ã",Auml:"Ä",auml:"ä",bdquo:"„",Beta:"Β",beta:"β",brvbar:"¦",bull:"•",cap:"∩",Ccedil:"Ç",ccedil:"ç",cedil:"¸",cent:"¢",Chi:"Χ",chi:"χ",circ:"ˆ",clubs:"♣",cong:"≅",copy:"©",crarr:"↵",cup:"∪",curren:"¤",dagger:"†",Dagger:"‡",darr:"↓",dArr:"⇓",deg:"°",Delta:"Δ",delta:"δ",diams:"♦",divide:"÷",Eacute:"É",eacute:"é",Ecirc:"Ê",ecirc:"ê",Egrave:"È",egrave:"è",empty:"∅",emsp:" ",ensp:" ",Epsilon:"Ε",epsilon:"ε",equiv:"≡",Eta:"Η",eta:"η",ETH:"Ð",eth:"ð",Euml:"Ë",euml:"ë",euro:"€",exist:"∃",fnof:"ƒ",forall:"∀",frac12:"½",frac14:"¼",frac34:"¾",frasl:"⁄",Gamma:"Γ",gamma:"γ",ge:"≥",gt:">",harr:"↔",hArr:"⇔",hearts:"♥",hellip:"…",Iacute:"Í",iacute:"í",Icirc:"Î",icirc:"î",iexcl:"¡",Igrave:"Ì",igrave:"ì",image:"ℑ",infin:"∞","int":"∫",Iota:"Ι",iota:"ι",iquest:"¿",isin:"∈",Iuml:"Ï",iuml:"ï",Kappa:"Κ",kappa:"κ",Lambda:"Λ",lambda:"λ",lang:"⟨",laquo:"«",larr:"←",lArr:"⇐",lceil:"⌈",ldquo:"“",le:"≤",lfloor:"⌊",lowast:"∗",loz:"◊",lrm:"‎",lsaquo:"‹",lsquo:"‘",lt:"<",macr:"¯",mdash:"—",micro:"µ",middot:"·",minus:"−",Mu:"Μ",mu:"μ",nabla:"∇",nbsp:" ",ndash:"–",ne:"≠",ni:"∋",not:"¬",notin:"∉",nsub:"⊄",Ntilde:"Ñ",ntilde:"ñ",Nu:"Ν",nu:"ν",Oacute:"Ó",oacute:"ó",Ocirc:"Ô",ocirc:"ô",OElig:"Œ",oelig:"œ",Ograve:"Ò",ograve:"ò",oline:"‾",Omega:"Ω",omega:"ω",Omicron:"Ο",omicron:"ο",oplus:"⊕",or:"∨",ordf:"ª",ordm:"º",Oslash:"Ø",oslash:"ø",Otilde:"Õ",otilde:"õ",otimes:"⊗",Ouml:"Ö",ouml:"ö",para:"¶",permil:"‰",perp:"⊥",Phi:"Φ",phi:"φ",Pi:"Π",pi:"π",piv:"ϖ",plusmn:"±",pound:"£",prime:"′",Prime:"″",prod:"∏",prop:"∝",Psi:"Ψ",psi:"ψ",quot:'"',radic:"√",rang:"⟩",raquo:"»",rarr:"→",rArr:"⇒",rceil:"⌉",rdquo:"”",real:"ℜ",reg:"®",rfloor:"⌋",Rho:"Ρ",rho:"ρ",rlm:"‏",rsaquo:"›",rsquo:"’",sbquo:"‚",Scaron:"Š",scaron:"š",sdot:"⋅",sect:"§",shy:"­",Sigma:"Σ",sigma:"σ",sigmaf:"ς",sim:"∼",spades:"♠",sub:"⊂",sube:"⊆",sum:"∑",sup:"⊃",sup1:"¹",sup2:"²",sup3:"³",supe:"⊇",szlig:"ß",Tau:"Τ",tau:"τ",there4:"∴",Theta:"Θ",theta:"θ",thetasym:"ϑ",thinsp:" ",THORN:"Þ",thorn:"þ",tilde:"˜",times:"×",trade:"™",Uacute:"Ú",uacute:"ú",uarr:"↑",uArr:"⇑",Ucirc:"Û",ucirc:"û",Ugrave:"Ù",ugrave:"ù",uml:"¨",upsih:"ϒ",Upsilon:"Υ",upsilon:"υ",Uuml:"Ü",uuml:"ü",weierp:"℘",Xi:"Ξ",xi:"ξ",Yacute:"Ý",yacute:"ý",yen:"¥",yuml:"ÿ",Yuml:"Ÿ",Zeta:"Ζ",zeta:"ζ",zwj:"‍",zwnj:"‌"}},function(e,t,n){"use strict";function createUrlResolverWithoutPackagePrefix(){return new s}function createOfflineCompileUrlResolver(){return new s(o)}function getUrlScheme(e){var t=_split(e);return t&&t[a.Scheme]||""}function _buildFromEncodedParts(e,t,n,r,o,s,a){var l=[];return i.isPresent(e)&&l.push(e+":"),i.isPresent(n)&&(l.push("//"),i.isPresent(t)&&l.push(t+"@"),l.push(n),i.isPresent(r)&&l.push(":"+r)),i.isPresent(o)&&l.push(o),i.isPresent(s)&&l.push("?"+s),i.isPresent(a)&&l.push("#"+a),l.join("")}function _split(e){return e.match(l)}function _removeDotSegments(e){if("/"==e)return"/";for(var t="/"==e[0]?"/":"",n="/"===e[e.length-1]?"/":"",r=e.split("/"),i=[],o=0,s=0;s0?i.pop():o++;break;default:i.push(a)}}if(""==t){for(;o-- >0;)i.unshift("..");0===i.length&&i.push(".")}return t+i.join("/")+n}function _joinAndCanonicalizePath(e){var t=e[a.Path];return t=i.isBlank(t)?"":_removeDotSegments(t),e[a.Path]=t,_buildFromEncodedParts(e[a.Scheme],e[a.UserInfo],e[a.Domain],e[a.Port],t,e[a.QueryData],e[a.Fragment])}function _resolveUrl(e,t){var n=_split(encodeURI(t)),r=_split(e);if(i.isPresent(n[a.Scheme]))return _joinAndCanonicalizePath(n);n[a.Scheme]=r[a.Scheme];for(var o=a.Scheme;o<=a.Port;o++)i.isBlank(n[o])&&(n[o]=r[o]);if("/"==n[a.Path][0])return _joinAndCanonicalizePath(n);var s=r[a.Path];i.isBlank(s)&&(s="/");var l=s.lastIndexOf("/");return s=s.substring(0,l+1)+n[a.Path],n[a.Path]=s,_joinAndCanonicalizePath(n)}var r=n(0),i=n(5),o="asset:";t.createUrlResolverWithoutPackagePrefix=createUrlResolverWithoutPackagePrefix,t.createOfflineCompileUrlResolver=createOfflineCompileUrlResolver,t.DEFAULT_PACKAGE_URL_PROVIDER={provide:r.PACKAGE_ROOT_URL,useValue:"/"};var s=function(){function UrlResolver(e){void 0===e&&(e=null),this._packagePrefix=e}return UrlResolver.prototype.resolve=function(e,t){var n=t;i.isPresent(e)&&e.length>0&&(n=_resolveUrl(e,n));var r=_split(n),s=this._packagePrefix;if(i.isPresent(s)&&i.isPresent(r)&&"package"==r[a.Scheme]){var l=r[a.Path];if(this._packagePrefix!==o)return s=i.StringWrapper.stripRight(s,"/"),l=i.StringWrapper.stripLeft(l,"/"),s+"/"+l;var c=l.split(/\//);n="asset:"+c[0]+"/lib/"+c.slice(1).join("/")}return n},UrlResolver.decorators=[{type:r.Injectable}],UrlResolver.ctorParameters=[{type:void 0,decorators:[{type:r.Inject,args:[r.PACKAGE_ROOT_URL]}]}],UrlResolver}();t.UrlResolver=s,t.getUrlScheme=getUrlScheme;var a,l=new RegExp("^(?:([^:/?#.]+):)?(?://(?:([^/?#]*)@)?([\\w\\d\\-\\u0100-\\uffff.%]*)(?::([0-9]+))?)?([^?#]+)?(?:\\?([^#]*))?(?:#(.*))?$");!function(e){e[e.Scheme=1]="Scheme",e[e.UserInfo=2]="UserInfo",e[e.Domain=3]="Domain",e[e.Port=4]="Port",e[e.Path=5]="Path",e[e.QueryData=6]="QueryData",e[e.Fragment=7]="Fragment"}(a||(a={}))},function(e,t,n){"use strict";function _enumExpression(e,t){if(s.isBlank(t))return l.NULL_EXPR;var n=s.resolveEnumToken(e.runtime,t);return l.importExpr(new o.CompileIdentifierMetadata({name:e.name+"."+n,moduleUrl:e.moduleUrl,runtime:t}))}var r=n(0),i=n(27),o=n(31),s=n(5),a=n(28),l=n(16),c=function(){function ViewTypeEnum(){}return ViewTypeEnum.fromValue=function(e){return _enumExpression(a.Identifiers.ViewType,e)},ViewTypeEnum.HOST=ViewTypeEnum.fromValue(i.ViewType.HOST),ViewTypeEnum.COMPONENT=ViewTypeEnum.fromValue(i.ViewType.COMPONENT),ViewTypeEnum.EMBEDDED=ViewTypeEnum.fromValue(i.ViewType.EMBEDDED),ViewTypeEnum}();t.ViewTypeEnum=c;var u=function(){function ViewEncapsulationEnum(){}return ViewEncapsulationEnum.fromValue=function(e){return _enumExpression(a.Identifiers.ViewEncapsulation,e)},ViewEncapsulationEnum.Emulated=ViewEncapsulationEnum.fromValue(r.ViewEncapsulation.Emulated),ViewEncapsulationEnum.Native=ViewEncapsulationEnum.fromValue(r.ViewEncapsulation.Native),ViewEncapsulationEnum.None=ViewEncapsulationEnum.fromValue(r.ViewEncapsulation.None),ViewEncapsulationEnum}();t.ViewEncapsulationEnum=u;var p=function(){function ChangeDetectionStrategyEnum(){}return ChangeDetectionStrategyEnum.fromValue=function(e){return _enumExpression(a.Identifiers.ChangeDetectionStrategy,e)},ChangeDetectionStrategyEnum.OnPush=ChangeDetectionStrategyEnum.fromValue(r.ChangeDetectionStrategy.OnPush),ChangeDetectionStrategyEnum.Default=ChangeDetectionStrategyEnum.fromValue(r.ChangeDetectionStrategy.Default),ChangeDetectionStrategyEnum}();t.ChangeDetectionStrategyEnum=p;var d=function(){function ChangeDetectorStatusEnum(){}return ChangeDetectorStatusEnum.fromValue=function(e){return _enumExpression(a.Identifiers.ChangeDetectorStatus,e)},ChangeDetectorStatusEnum.CheckOnce=ChangeDetectorStatusEnum.fromValue(i.ChangeDetectorStatus.CheckOnce),ChangeDetectorStatusEnum.Checked=ChangeDetectorStatusEnum.fromValue(i.ChangeDetectorStatus.Checked),ChangeDetectorStatusEnum.CheckAlways=ChangeDetectorStatusEnum.fromValue(i.ChangeDetectorStatus.CheckAlways),ChangeDetectorStatusEnum.Detached=ChangeDetectorStatusEnum.fromValue(i.ChangeDetectorStatus.Detached),ChangeDetectorStatusEnum.Errored=ChangeDetectorStatusEnum.fromValue(i.ChangeDetectorStatus.Errored),ChangeDetectorStatusEnum.Destroyed=ChangeDetectorStatusEnum.fromValue(i.ChangeDetectorStatus.Destroyed),ChangeDetectorStatusEnum}();t.ChangeDetectorStatusEnum=d;var h=function(){function ViewConstructorVars(){}return ViewConstructorVars.viewUtils=l.variable("viewUtils"),ViewConstructorVars.parentInjector=l.variable("parentInjector"),ViewConstructorVars.declarationEl=l.variable("declarationEl"),ViewConstructorVars}();t.ViewConstructorVars=h;var f=function(){function ViewProperties(){}return ViewProperties.renderer=l.THIS_EXPR.prop("renderer"),ViewProperties.projectableNodes=l.THIS_EXPR.prop("projectableNodes"),ViewProperties.viewUtils=l.THIS_EXPR.prop("viewUtils"),ViewProperties}();t.ViewProperties=f;var m=function(){function EventHandlerVars(){}return EventHandlerVars.event=l.variable("$event"),EventHandlerVars}();t.EventHandlerVars=m;var y=function(){function InjectMethodVars(){}return InjectMethodVars.token=l.variable("token"),InjectMethodVars.requestNodeIndex=l.variable("requestNodeIndex"),InjectMethodVars.notFoundResult=l.variable("notFoundResult"),InjectMethodVars}();t.InjectMethodVars=y;var v=function(){function DetectChangesVars(){}return DetectChangesVars.throwOnChange=l.variable("throwOnChange"),DetectChangesVars.changes=l.variable("changes"),DetectChangesVars.changed=l.variable("changed"),DetectChangesVars.valUnwrapper=l.variable("valUnwrapper"),DetectChangesVars}();t.DetectChangesVars=v},100,function(e,t,n){var r=n(95);e.exports=function(e,t,n){if(r(e),void 0===t)return e;switch(n){case 1:return function(n){return e.call(t,n)};case 2:return function(n,r){return e.call(t,n,r)};case 3:return function(n,r,i){return e.call(t,n,r,i)}}return function(){return e.apply(t,arguments)}}},function(e,t,n){var r=n(8),i=n(465),o=n(288),s=n(301)("IE_PROTO"),a=function(){},l="prototype",c=function(){var e,t=n(454)("iframe"),r=o.length,i="<",s=">";for(t.style.display="none",n(455).appendChild(t),t.src="javascript:",e=t.contentWindow.document,e.open(),e.write(i+"script"+s+"document.F=Object"+i+"/script"+s),e.close(),c=e.F;r--;)delete c[l][o[r]];return c()};e.exports=Object.create||function(e,t){var n;return null!==e?(a[l]=r(e),n=new a,a[l]=null,n[s]=e):n=c(),void 0===t?n:i(n,t)}},function(e,t,n){var r=n(467),i=n(288);e.exports=Object.keys||function(e){return r(e,i)}},function(e,t){var n=Math.ceil,r=Math.floor;e.exports=function(e){return isNaN(e=+e)?0:(e>0?r:n)(e)}},function(e,t,n){"use strict";function multicast(e){var t;return t="function"==typeof e?e:function(){return e},new r.ConnectableObservable(this,t)}var r=n(503);t.multicast=multicast},function(e,t,n){"use strict";var r=n(44),i=(n.n(r),n(0));n.n(i);n.d(t,"a",function(){return a});var o=this&&this.__decorate||function(e,t,n,r){var i,o=arguments.length,s=o<3?t:null===r?r=Object.getOwnPropertyDescriptor(t,n):r;if("object"==typeof Reflect&&"function"==typeof Reflect.decorate)s=Reflect.decorate(e,t,n,r);else for(var a=e.length-1;a>=0;a--)(i=e[a])&&(s=(o<3?i(s):o>3?i(t,n,s):i(t,n))||s);return o>3&&s&&Object.defineProperty(t,n,s),s},s=this&&this.__metadata||function(e,t){if("object"==typeof Reflect&&"function"==typeof Reflect.metadata)return Reflect.metadata(e,t)},a=function(){function FeaturesService(e){var t=this;this.http=e,this.activeReparents=!1,this.showStatus=!1,this.showTopologyCRUD=!1,this.showWorkflows=!1,this.workflows=[],this.featuresUrl="../api/features",this.getFeatures().subscribe(function(e){t.activeReparents=e.activeReparents,t.showStatus=e.showStatus,t.showTopologyCRUD=e.showTopologyCRUD,t.showWorkflows=e.showWorkflows,t.workflows=e.workflows})}return FeaturesService.prototype.getFeatures=function(){return this.http.get(this.featuresUrl).map(function(e){return e.json()})},FeaturesService=o([n.i(i.Injectable)(),s("design:paramtypes",["function"==typeof(e="undefined"!=typeof r.Http&&r.Http)&&e||Object])],FeaturesService);var e}()},function(e,t,n){"use strict";var r=n(44),i=(n.n(r),n(0)),o=(n.n(i),n(485)),s=(n.n(o),n(215)),a=n(521),l=n(216);n.d(t,"a",function(){return p});var c=this&&this.__decorate||function(e,t,n,r){var i,o=arguments.length,s=o<3?t:null===r?r=Object.getOwnPropertyDescriptor(t,n):r;if("object"==typeof Reflect&&"function"==typeof Reflect.decorate)s=Reflect.decorate(e,t,n,r);else for(var a=e.length-1;a>=0;a--)(i=e[a])&&(s=(o<3?i(s):o>3?i(t,n,s):i(t,n))||s);return o>3&&s&&Object.defineProperty(t,n,s),s},u=this&&this.__metadata||function(e,t){if("object"==typeof Reflect&&"function"==typeof Reflect.metadata)return Reflect.metadata(e,t)},p=function(){function KeyspaceService(e,t){this.http=e,this.shardService=t,this.keyspacesUrl="../api/keyspaces/",this.srvKeyspaceUrl="../api/srv_keyspace/local/"}return KeyspaceService.prototype.getShards=function(e){return this.shardService.getShards(e)},KeyspaceService.prototype.getKeyspaceNames=function(){return this.http.get(this.keyspacesUrl).map(function(e){return e.json()})},KeyspaceService.prototype.getSrvKeyspaces=function(){return this.http.get(this.srvKeyspaceUrl).map(function(e){return e.json()})},KeyspaceService.prototype.SrvKeyspaceAndNamesObservable=function(){var e=this.getKeyspaceNames(),t=this.getSrvKeyspaces();return e.combineLatest(t)},KeyspaceService.prototype.getKeyspaceShardingData=function(e){return this.http.get(this.keyspacesUrl+e).map(function(e){return e.json()})},KeyspaceService.prototype.getShardsAndShardingData=function(e){var t=this.getShards(e),n=this.getKeyspaceShardingData(e);return t.combineLatest(n)},KeyspaceService.prototype.buildKeyspace=function(e,t){return this.getShardsAndShardingData(e).map(function(n){var r=n[0],i=n[1],o=new a.a(e);return t.forEach(function(e){return o.addServingShard(e)}),r.forEach(function(e){o.contains(e)||o.addNonservingShard(e)}),o.shardingColumnName=i.sharding_column_name||"",o.shardingColumnType=i.sharding_column_type||"",o})},KeyspaceService.prototype.getServingShards=function(e,t){if(t&&t[e]){var n=t[e].partitions;if(void 0===n)return[];for(var r=0;r0&&(n=n.callMethod(s.BuiltinMethod.ConcatArray,[s.literalArr(t)]),t=[]),n=n.callMethod(s.BuiltinMethod.ConcatArray,[i])):t.push(i)}return t.length>0&&(n=n.callMethod(s.BuiltinMethod.ConcatArray,[s.literalArr(t)])),n}function createPureProxy(e,t,n,a){a.fields.push(new s.ClassField(n.name,null));var l=t0){var r=e.substring(0,n),i=e.substring(n+1).trim();t.set(r,i)}}),t},Headers.prototype.append=function(e,t){e=normalize(e);var n=this._headersMap.get(e),i=r.isListLikeIterable(n)?n:[];i.push(t),this._headersMap.set(e,i)},Headers.prototype.delete=function(e){this._headersMap.delete(normalize(e))},Headers.prototype.forEach=function(e){this._headersMap.forEach(e)},Headers.prototype.get=function(e){return r.ListWrapper.first(this._headersMap.get(normalize(e)))},Headers.prototype.has=function(e){return this._headersMap.has(normalize(e))},Headers.prototype.keys=function(){return r.MapWrapper.keys(this._headersMap)},Headers.prototype.set=function(e,t){var n=[];if(r.isListLikeIterable(t)){var i=t.join(",");n.push(i)}else n.push(t);this._headersMap.set(normalize(e),n)},Headers.prototype.values=function(){return r.MapWrapper.values(this._headersMap)},Headers.prototype.toJSON=function(){var e={};return this._headersMap.forEach(function(t,n){var i=[];r.iterateListLike(t,function(e){return i=r.ListWrapper.concat(i,e.split(","))}),e[normalize(n)]=i}),e},Headers.prototype.getAll=function(e){var t=this._headersMap.get(normalize(e));return r.isListLikeIterable(t)?t:[]},Headers.prototype.entries=function(){throw new i.BaseException('"entries" method is not implemented on Headers class')},Headers}();t.Headers=s},function(e,t){"use strict";var n=function(){function ConnectionBackend(){}return ConnectionBackend}();t.ConnectionBackend=n;var r=function(){function Connection(){}return Connection}();t.Connection=r;var i=function(){function XSRFStrategy(){}return XSRFStrategy}();t.XSRFStrategy=i},function(e,t,n){"use strict";var r=n(0);t.RenderDebugInfo=r.__core_private__.RenderDebugInfo,t.wtfInit=r.__core_private__.wtfInit,t.ReflectionCapabilities=r.__core_private__.ReflectionCapabilities,t.VIEW_ENCAPSULATION_VALUES=r.__core_private__.VIEW_ENCAPSULATION_VALUES,t.DebugDomRootRenderer=r.__core_private__.DebugDomRootRenderer,t.reflector=r.__core_private__.reflector,t.NoOpAnimationPlayer=r.__core_private__.NoOpAnimationPlayer,t.AnimationPlayer=r.__core_private__.AnimationPlayer,t.AnimationSequencePlayer=r.__core_private__.AnimationSequencePlayer,t.AnimationGroupPlayer=r.__core_private__.AnimationGroupPlayer,t.AnimationKeyframe=r.__core_private__.AnimationKeyframe,t.AnimationStyles=r.__core_private__.AnimationStyles,t.prepareFinalAnimationStyles=r.__core_private__.prepareFinalAnimationStyles,t.balanceAnimationKeyframes=r.__core_private__.balanceAnimationKeyframes,t.flattenStyles=r.__core_private__.flattenStyles,t.clearStyles=r.__core_private__.clearStyles,t.collectAndResolveStyles=r.__core_private__.collectAndResolveStyles},function(e,t,n){"use strict";var r=n(0);t.DOCUMENT=new r.OpaqueToken("DocumentToken")},function(e,t,n){"use strict";var r=this&&this.__extends||function(e,t){function __(){this.constructor=e}for(var n in t)t.hasOwnProperty(n)&&(e[n]=t[n]);e.prototype=null===t?Object.create(t):(__.prototype=t.prototype,new __)},i=n(0),o=n(33),s=n(15),a=n(62),l=n(56),c=function(){function ClientMessageBrokerFactory(){}return ClientMessageBrokerFactory}();t.ClientMessageBrokerFactory=c;var u=function(e){function ClientMessageBrokerFactory_(t,n){e.call(this),this._messageBus=t,this._serializer=n}return r(ClientMessageBrokerFactory_,e),ClientMessageBrokerFactory_.prototype.createMessageBroker=function(e,t){return void 0===t&&(t=!0),this._messageBus.initChannel(e,t),new d(this._messageBus,this._serializer,e)},ClientMessageBrokerFactory_.decorators=[{type:i.Injectable}],ClientMessageBrokerFactory_.ctorParameters=[{type:a.MessageBus},{type:l.Serializer}],ClientMessageBrokerFactory_}(c);t.ClientMessageBrokerFactory_=u;var p=function(){function ClientMessageBroker(){}return ClientMessageBroker}();t.ClientMessageBroker=p;var d=function(e){function ClientMessageBroker_(t,n,r){var i=this;e.call(this),this.channel=r,this._pending=new Map,this._sink=t.to(r),this._serializer=n;var o=t.from(r);o.subscribe({next:function(e){return i._handleMessage(e)}})}return r(ClientMessageBroker_,e),ClientMessageBroker_.prototype._generateMessageId=function(e){for(var t=s.stringify(s.DateWrapper.toMillis(s.DateWrapper.now())),n=0,r=e+t+s.stringify(n);s.isPresent(this._pending[r]);)r=""+e+t+n,n++;return r},ClientMessageBroker_.prototype.runOnService=function(e,t){var n=this,r=[];s.isPresent(e.args)&&e.args.forEach(function(e){null!=e.type?r.push(n._serializer.serialize(e.value,e.type)):r.push(e.value)});var i,o=null;if(null!=t){var a;i=new Promise(function(e,t){a={resolve:e,reject:t}}),o=this._generateMessageId(e.method),this._pending.set(o,a),i.catch(function(e){s.print(e),a.reject(e)}),i=i.then(function(e){return null==n._serializer?e:n._serializer.deserialize(e,t)})}else i=null;var l={method:e.method,args:r};return null!=o&&(l.id=o),this._sink.emit(l),i},ClientMessageBroker_.prototype._handleMessage=function(e){var t=new h(e);if(s.StringWrapper.equals(t.type,"result")||s.StringWrapper.equals(t.type,"error")){var n=t.id;this._pending.has(n)&&(s.StringWrapper.equals(t.type,"result")?this._pending.get(n).resolve(t.value):this._pending.get(n).reject(t.value),this._pending.delete(n))}},ClientMessageBroker_}(p);t.ClientMessageBroker_=d;var h=function(){function MessageData(e){this.type=o.StringMapWrapper.get(e,"type"),this.id=this._getValueIfPresent(e,"id"),this.value=this._getValueIfPresent(e,"value")}return MessageData.prototype._getValueIfPresent=function(e,t){return o.StringMapWrapper.contains(e,t)?o.StringMapWrapper.get(e,t):null},MessageData}(),f=function(){function FnArg(e,t){this.value=e,this.type=t}return FnArg}();t.FnArg=f;var m=function(){function UiArguments(e,t){this.method=e,this.args=t}return UiArguments}();t.UiArguments=m},function(e,t,n){"use strict";var r=n(0),i=function(){function RenderStore(){this._nextIndex=0,this._lookupById=new Map,this._lookupByObject=new Map}return RenderStore.prototype.allocateId=function(){return this._nextIndex++},RenderStore.prototype.store=function(e,t){this._lookupById.set(t,e),this._lookupByObject.set(e,t)},RenderStore.prototype.remove=function(e){var t=this._lookupByObject.get(e);this._lookupByObject.delete(e),this._lookupById.delete(t)},RenderStore.prototype.deserialize=function(e){return null==e?null:this._lookupById.has(e)?this._lookupById.get(e):null},RenderStore.prototype.serialize=function(e){return null==e?null:this._lookupByObject.get(e)},RenderStore.decorators=[{type:r.Injectable}],RenderStore.ctorParameters=[],RenderStore}();t.RenderStore=i},function(e,t,n){"use strict";var r=this&&this.__extends||function(e,t){function __(){this.constructor=e}for(var n in t)t.hasOwnProperty(n)&&(e[n]=t[n]);e.prototype=null===t?Object.create(t):(__.prototype=t.prototype,new __)},i=n(0),o=n(33),s=n(15),a=n(62),l=n(56),c=function(){function ServiceMessageBrokerFactory(){}return ServiceMessageBrokerFactory}();t.ServiceMessageBrokerFactory=c;var u=function(e){function ServiceMessageBrokerFactory_(t,n){e.call(this),this._messageBus=t,this._serializer=n}return r(ServiceMessageBrokerFactory_,e),ServiceMessageBrokerFactory_.prototype.createMessageBroker=function(e,t){return void 0===t&&(t=!0),this._messageBus.initChannel(e,t),new d(this._messageBus,this._serializer,e)},ServiceMessageBrokerFactory_.decorators=[{type:i.Injectable}],ServiceMessageBrokerFactory_.ctorParameters=[{type:a.MessageBus},{type:l.Serializer}],ServiceMessageBrokerFactory_}(c);t.ServiceMessageBrokerFactory_=u;var p=function(){function ServiceMessageBroker(){}return ServiceMessageBroker}();t.ServiceMessageBroker=p;var d=function(e){function ServiceMessageBroker_(t,n,r){var i=this;e.call(this),this._serializer=n,this.channel=r,this._methods=new o.Map,this._sink=t.to(r);var s=t.from(r);s.subscribe({next:function(e){return i._handleMessage(e)}})}return r(ServiceMessageBroker_,e),ServiceMessageBroker_.prototype.registerMethod=function(e,t,n,r){var i=this;this._methods.set(e,function(e){for(var a=e.args,l=null===t?0:t.length,c=o.ListWrapper.createFixedSize(l),u=0;u0?n[n.length-1]._routeConfig._loadedConfig:null}function nodeChildrenAsMap(e){return e?e.children.reduce(function(e,t){return e[t.value.outlet]=t,e},{}):{}}function getOutlet(e,t){var n=e._outlets[t.outlet];if(!n){var r=t.component.name;throw t.outlet===y.PRIMARY_OUTLET?new Error("Cannot find primary outlet to load '"+r+"'"):new Error("Cannot find the outlet "+t.outlet+" to load '"+r+"'")}return n}n(139),n(500),n(306),n(501),n(494);var r=n(0),i=n(20),o=n(310),s=n(140),a=n(671),l=n(672),c=n(673),u=n(674),p=n(675),d=n(676),h=n(190),f=n(131),m=n(94),y=n(63),v=n(76),g=n(77),b=function(){function NavigationStart(e,t){this.id=e,this.url=t}return NavigationStart.prototype.toString=function(){return"NavigationStart(id: "+this.id+", url: '"+this.url+"')"},NavigationStart}();t.NavigationStart=b;var _=function(){function NavigationEnd(e,t,n){this.id=e,this.url=t,this.urlAfterRedirects=n}return NavigationEnd.prototype.toString=function(){return"NavigationEnd(id: "+this.id+", url: '"+this.url+"', urlAfterRedirects: '"+this.urlAfterRedirects+"')"},NavigationEnd}();t.NavigationEnd=_;var S=function(){function NavigationCancel(e,t){this.id=e,this.url=t}return NavigationCancel.prototype.toString=function(){return"NavigationCancel(id: "+this.id+", url: '"+this.url+"')"},NavigationCancel}();t.NavigationCancel=S;var w=function(){function NavigationError(e,t,n){this.id=e,this.url=t,this.error=n}return NavigationError.prototype.toString=function(){return"NavigationError(id: "+this.id+", url: '"+this.url+"', error: "+this.error+")"},NavigationError}();t.NavigationError=w;var C=function(){function RoutesRecognized(e,t,n,r){this.id=e,this.url=t,this.urlAfterRedirects=n,this.state=r}return RoutesRecognized.prototype.toString=function(){return"RoutesRecognized(id: "+this.id+", url: '"+this.url+"', urlAfterRedirects: '"+this.urlAfterRedirects+"', state: "+this.state+")"},RoutesRecognized}();t.RoutesRecognized=C;var E=function(){function Router(e,t,n,r,o,s,a,l){this.rootComponentType=e,this.resolver=t,this.urlSerializer=n,this.outletMap=r,this.location=o,this.injector=s,this.navigationId=0,this.navigated=!1,this.resetConfig(l),this.routerEvents=new i.Subject,this.currentUrlTree=v.createEmptyUrlTree(),this.configLoader=new h.RouterConfigLoader(a),this.currentRouterState=m.createEmptyState(this.currentUrlTree,this.rootComponentType)}return Router.prototype.initialNavigation=function(){this.setUpLocationChangeListener(),this.navigateByUrl(this.location.path(!0))},Object.defineProperty(Router.prototype,"routerState",{get:function(){return this.currentRouterState},enumerable:!0,configurable:!0}),Object.defineProperty(Router.prototype,"url",{get:function(){return this.serializeUrl(this.currentUrlTree)},enumerable:!0,configurable:!0}),Object.defineProperty(Router.prototype,"events",{get:function(){return this.routerEvents},enumerable:!0,configurable:!0}),Router.prototype.resetConfig=function(e){l.validateConfig(e),this.config=e},Router.prototype.ngOnDestroy=function(){this.dispose()},Router.prototype.dispose=function(){this.locationSubscription.unsubscribe()},Router.prototype.createUrlTree=function(e,t){var n=void 0===t?{}:t,r=n.relativeTo,i=n.queryParams,o=n.fragment,s=n.preserveQueryParams,a=n.preserveFragment,l=r?r:this.routerState.root,c=s?this.currentUrlTree.queryParams:i,p=a?this.currentUrlTree.fragment:o;return u.createUrlTree(l,this.currentUrlTree,e,c,p)},Router.prototype.navigateByUrl=function(e,t){if(void 0===t&&(t={skipLocationChange:!1}),e instanceof v.UrlTree)return this.scheduleNavigation(e,t);var n=this.urlSerializer.parse(e);return this.scheduleNavigation(n,t)},Router.prototype.navigate=function(e,t){return void 0===t&&(t={skipLocationChange:!1}),this.scheduleNavigation(this.createUrlTree(e,t),t)},Router.prototype.serializeUrl=function(e){return this.urlSerializer.serialize(e)},Router.prototype.parseUrl=function(e){return this.urlSerializer.parse(e)},Router.prototype.isActive=function(e,t){if(e instanceof v.UrlTree)return v.containsTree(this.currentUrlTree,e,t);var n=this.urlSerializer.parse(e);return v.containsTree(this.currentUrlTree,n,t)},Router.prototype.scheduleNavigation=function(e,t){var n=this,r=++this.navigationId;return this.routerEvents.next(new b(r,this.serializeUrl(e))),Promise.resolve().then(function(i){return n.runNavigate(e,t.skipLocationChange,r)})},Router.prototype.setUpLocationChangeListener=function(){var e=this;this.locationSubscription=this.location.subscribe(Zone.current.wrap(function(t){var n=e.urlSerializer.parse(t.url);return e.currentUrlTree.toString()!==n.toString()?e.scheduleNavigation(n,t.pop):null}))},Router.prototype.runNavigate=function(e,t,n){var r=this;return n!==this.navigationId?(this.location.go(this.urlSerializer.serialize(this.currentUrlTree)),this.routerEvents.next(new S(n,this.serializeUrl(e))),Promise.resolve(!1)):new Promise(function(i,o){var l,u,h,f,m=r.currentRouterState,y=r.currentUrlTree;a.applyRedirects(r.injector,r.configLoader,e,r.config).mergeMap(function(e){return f=e,p.recognize(r.rootComponentType,r.config,f,r.serializeUrl(f))}).mergeMap(function(t){return r.routerEvents.next(new C(n,r.serializeUrl(e),r.serializeUrl(f),t)),d.resolve(r.resolver,t)}).map(function(e){return c.createRouterState(e,r.currentRouterState)}).map(function(e){l=e,h=new P(l.snapshot,r.currentRouterState.snapshot,r.injector),h.traverse(r.outletMap)}).mergeMap(function(e){return h.checkGuards()}).mergeMap(function(e){return e?h.resolveData().map(function(){return e}):s.of(e)}).forEach(function(i){if(!i||n!==r.navigationId)return r.routerEvents.next(new S(n,r.serializeUrl(e))),void(u=!1);if(r.currentUrlTree=f,r.currentRouterState=l,new x(l,m).activate(r.outletMap),!t){var o=r.urlSerializer.serialize(f);r.location.isCurrentPathEqualTo(o)?r.location.replaceState(o):r.location.go(o)}u=!0}).then(function(){r.navigated=!0,r.routerEvents.next(new _(n,r.serializeUrl(e),r.serializeUrl(f))),i(u)},function(t){r.currentRouterState=m,r.currentUrlTree=y,r.routerEvents.next(new w(n,r.serializeUrl(e),t)),o(t)})})},Router}();t.Router=E;var T=function(){function CanActivate(e){this.path=e}return Object.defineProperty(CanActivate.prototype,"route",{get:function(){return this.path[this.path.length-1]},enumerable:!0,configurable:!0}),CanActivate}(),R=function(){function CanDeactivate(e,t){this.component=e,this.route=t}return CanDeactivate}(),P=function(){function PreActivation(e,t,n){this.future=e,this.curr=t,this.injector=n,this.checks=[]}return PreActivation.prototype.traverse=function(e){var t=this.future._root,n=this.curr?this.curr._root:null;this.traverseChildRoutes(t,n,e,[t.value])},PreActivation.prototype.checkGuards=function(){var e=this;return 0===this.checks.length?s.of(!0):o.from(this.checks).map(function(t){ -if(t instanceof T)return g.andObservables(o.from([e.runCanActivate(t.route),e.runCanActivateChild(t.path)]));if(t instanceof R){var n=t;return e.runCanDeactivate(n.component,n.route)}throw new Error("Cannot be reached")}).mergeAll().every(function(e){return e===!0})},PreActivation.prototype.resolveData=function(){var e=this;return 0===this.checks.length?s.of(null):o.from(this.checks).mergeMap(function(t){return t instanceof T?e.runResolve(t.route):s.of(null)}).reduce(function(e,t){return e})},PreActivation.prototype.traverseChildRoutes=function(e,t,n,r){var i=this,o=nodeChildrenAsMap(t);e.children.forEach(function(e){i.traverseRoutes(e,o[e.value.outlet],n,r.concat([e.value])),delete o[e.value.outlet]}),g.forEach(o,function(e,t){return i.deactivateOutletAndItChildren(e,n._outlets[t])})},PreActivation.prototype.traverseRoutes=function(e,t,n,r){var i=e.value,o=t?t.value:null,s=n?n._outlets[e.value.outlet]:null;o&&i._routeConfig===o._routeConfig?(g.shallowEqual(i.params,o.params)||this.checks.push(new R(s.component,o),new T(r)),i.component?this.traverseChildRoutes(e,t,s?s.outletMap:null,r):this.traverseChildRoutes(e,t,n,r)):(o&&(o.component?this.deactivateOutletAndItChildren(o,s):this.deactivateOutletMap(n)),this.checks.push(new T(r)),i.component?this.traverseChildRoutes(e,null,s?s.outletMap:null,r):this.traverseChildRoutes(e,null,n,r))},PreActivation.prototype.deactivateOutletAndItChildren=function(e,t){t&&t.isActivated&&(this.deactivateOutletMap(t.outletMap),this.checks.push(new R(t.component,e)))},PreActivation.prototype.deactivateOutletMap=function(e){var t=this;g.forEach(e._outlets,function(e){e.isActivated&&t.deactivateOutletAndItChildren(e.activatedRoute.snapshot,e)})},PreActivation.prototype.runCanActivate=function(e){var t=this,n=e._routeConfig?e._routeConfig.canActivate:null;if(!n||0===n.length)return s.of(!0);var r=o.from(n).map(function(n){var r=t.getToken(n,e,t.future);return r.canActivate?g.wrapIntoObservable(r.canActivate(e,t.future)):g.wrapIntoObservable(r(e,t.future))});return g.andObservables(r)},PreActivation.prototype.runCanActivateChild=function(e){var t=this,n=e[e.length-1],r=e.slice(0,e.length-1).reverse().map(function(e){return t.extractCanActivateChild(e)}).filter(function(e){return null!==e});return g.andObservables(o.from(r).map(function(e){var r=o.from(e.guards).map(function(e){var r=t.getToken(e,e.node,t.future);return r.canActivateChild?g.wrapIntoObservable(r.canActivateChild(n,t.future)):g.wrapIntoObservable(r(n,t.future))});return g.andObservables(r)}))},PreActivation.prototype.extractCanActivateChild=function(e){var t=e._routeConfig?e._routeConfig.canActivateChild:null;return t&&0!==t.length?{node:e,guards:t}:null},PreActivation.prototype.runCanDeactivate=function(e,t){var n=this,r=t&&t._routeConfig?t._routeConfig.canDeactivate:null;return r&&0!==r.length?o.from(r).map(function(r){var i=n.getToken(r,t,n.curr);return i.canDeactivate?g.wrapIntoObservable(i.canDeactivate(e,t,n.curr)):g.wrapIntoObservable(i(e,t,n.curr))}).mergeAll().every(function(e){return e===!0}):s.of(!0)},PreActivation.prototype.runResolve=function(e){var t=e._resolve;return this.resolveNode(t.current,e).map(function(n){return t.resolvedData=n,e.data=g.merge(e.data,t.flattenedResolvedData),null})},PreActivation.prototype.resolveNode=function(e,t){var n=this;return g.waitForMap(e,function(e,r){var i=n.getToken(r,t,n.future);return i.resolve?g.wrapIntoObservable(i.resolve(t,n.future)):g.wrapIntoObservable(i(t,n.future))})},PreActivation.prototype.getToken=function(e,t,n){var r=closestLoadedConfig(n,t),i=r?r.injector:this.injector;return i.get(e)},PreActivation}(),x=function(){function ActivateRoutes(e,t){this.futureState=e,this.currState=t}return ActivateRoutes.prototype.activate=function(e){var t=this.futureState._root,n=this.currState?this.currState._root:null;m.advanceActivatedRoute(this.futureState.root),this.activateChildRoutes(t,n,e)},ActivateRoutes.prototype.activateChildRoutes=function(e,t,n){var r=this,i=nodeChildrenAsMap(t);e.children.forEach(function(e){r.activateRoutes(e,i[e.value.outlet],n),delete i[e.value.outlet]}),g.forEach(i,function(e,t){return r.deactivateOutletAndItChildren(n._outlets[t])})},ActivateRoutes.prototype.activateRoutes=function(e,t,n){var r=e.value,i=t?t.value:null;if(r===i)if(m.advanceActivatedRoute(r),r.component){var o=getOutlet(n,e.value);this.activateChildRoutes(e,t,o.outletMap)}else this.activateChildRoutes(e,t,n);else{if(i)if(i.component){var o=getOutlet(n,e.value);this.deactivateOutletAndItChildren(o)}else this.deactivateOutletMap(n);if(r.component){m.advanceActivatedRoute(r);var o=getOutlet(n,e.value),s=new f.RouterOutletMap;this.placeComponentIntoOutlet(s,r,o),this.activateChildRoutes(e,null,s)}else m.advanceActivatedRoute(r),this.activateChildRoutes(e,null,n)}},ActivateRoutes.prototype.placeComponentIntoOutlet=function(e,t,n){var i=[{provide:m.ActivatedRoute,useValue:t},{provide:f.RouterOutletMap,useValue:e}],o=closestLoadedConfig(this.futureState.snapshot,t.snapshot),s=null,a=null;o&&(s=o.factoryResolver,a=o.injector,i.push({provide:r.ComponentFactoryResolver,useValue:s})),n.activate(t,s,a,r.ReflectiveInjector.resolve(i),e)},ActivateRoutes.prototype.deactivateOutletAndItChildren=function(e){e&&e.isActivated&&(this.deactivateOutletMap(e.outletMap),e.deactivate())},ActivateRoutes.prototype.deactivateOutletMap=function(e){var t=this;g.forEach(e._outlets,function(e){return t.deactivateOutletAndItChildren(e)})},ActivateRoutes}()},function(e,t){"use strict";var n=function(){function RouterOutletMap(){this._outlets={}}return RouterOutletMap.prototype.registerOutlet=function(e,t){this._outlets[e]=t},RouterOutletMap.prototype.removeOutlet=function(e){this._outlets[e]=void 0},RouterOutletMap}();t.RouterOutletMap=n},function(e,t,n){var r=n(24)("unscopables"),i=Array.prototype;void 0==i[r]&&n(66)(i,r,{}),e.exports=function(e){i[r][e]=!0}},function(e,t,n){var r=n(96);e.exports=Object("z").propertyIsEnumerable(0)?Object:function(e){return"String"==r(e)?e.split(""):Object(e)}},function(e,t){e.exports={}},function(e,t,n){var r=n(467),i=n(288).concat("length","prototype");t.f=Object.getOwnPropertyNames||function(e){return r(e,i)}},function(e,t,n){var r=n(110),i=Math.max,o=Math.min;e.exports=function(e,t){return e=r(e),e<0?i(e+t,0):o(e,t)}},function(e,t){var n=0,r=Math.random();e.exports=function(e){return"Symbol(".concat(void 0===e?"":e,")_",(++n+r).toString(36))}},function(e,t,n){"use strict";var r=this&&this.__decorate||function(e,t,n,r){var i,o=arguments.length,s=o<3?t:null===r?r=Object.getOwnPropertyDescriptor(t,n):r;if("object"==typeof Reflect&&"function"==typeof Reflect.decorate)s=Reflect.decorate(e,t,n,r);else for(var a=e.length-1;a>=0;a--)(i=e[a])&&(s=(o<3?i(s):o>3?i(t,n,s):i(t,n))||s);return o>3&&s&&Object.defineProperty(t,n,s),s},i=this&&this.__metadata||function(e,t){if("object"==typeof Reflect&&"function"==typeof Reflect.metadata)return Reflect.metadata(e,t)},o=n(0),s=n(9),a=n(3),l=function(){function Button(e,t){this.el=e,this.domHandler=t,this.iconPos="left"}return Button.prototype.ngAfterViewInit=function(){if(this.domHandler.addMultipleClasses(this.el.nativeElement,this.getStyleClass()),this.icon){var e=document.createElement("span"),t="right"==this.iconPos?"ui-button-icon-right":"ui-button-icon-left";e.className=t+" ui-c fa fa-fw "+this.icon,this.el.nativeElement.appendChild(e)}var n=document.createElement("span");n.className="ui-button-text ui-c",n.appendChild(document.createTextNode(this.label||"ui-button")),this.el.nativeElement.appendChild(n),this.initialized=!0},Button.prototype.onMouseenter=function(e){this.hover=!0},Button.prototype.onMouseleave=function(e){this.hover=!1,this.active=!1},Button.prototype.onMouseDown=function(e){this.active=!0},Button.prototype.onMouseUp=function(e){this.active=!1},Button.prototype.onFocus=function(e){this.focus=!0},Button.prototype.onBlur=function(e){this.focus=!1},Button.prototype.isDisabled=function(){return this.el.nativeElement.disabled},Button.prototype.getStyleClass=function(){var e="ui-button ui-widget ui-state-default ui-corner-all";return e+=this.icon?null!=this.label&&void 0!=this.label?"left"==this.iconPos?" ui-button-text-icon-left":" ui-button-text-icon-right":" ui-button-icon-only":" ui-button-text-only"},Object.defineProperty(Button.prototype,"label",{get:function(){return this._label},set:function(e){this._label=e,this.initialized&&(this.domHandler.findSingle(this.el.nativeElement,".ui-button-text").textContent=this._label)},enumerable:!0,configurable:!0}),Button.prototype.ngOnDestroy=function(){for(;this.el.nativeElement.hasChildNodes();)this.el.nativeElement.removeChild(this.el.nativeElement.lastChild);this.initialized=!1},r([o.Input(),i("design:type",String)],Button.prototype,"icon",void 0),r([o.Input(),i("design:type",String)],Button.prototype,"iconPos",void 0),r([o.HostListener("mouseenter",["$event"]),i("design:type",Function),i("design:paramtypes",[Object]),i("design:returntype",void 0)],Button.prototype,"onMouseenter",null),r([o.HostListener("mouseleave",["$event"]),i("design:type",Function),i("design:paramtypes",[Object]),i("design:returntype",void 0)],Button.prototype,"onMouseleave",null),r([o.HostListener("mousedown",["$event"]),i("design:type",Function),i("design:paramtypes",[Object]),i("design:returntype",void 0)],Button.prototype,"onMouseDown",null),r([o.HostListener("mouseup",["$event"]),i("design:type",Function),i("design:paramtypes",[Object]),i("design:returntype",void 0)],Button.prototype,"onMouseUp",null),r([o.HostListener("focus",["$event"]),i("design:type",Function),i("design:paramtypes",[Object]),i("design:returntype",void 0)],Button.prototype,"onFocus",null),r([o.HostListener("blur",["$event"]),i("design:type",Function),i("design:paramtypes",[Object]),i("design:returntype",void 0)],Button.prototype,"onBlur",null),r([o.Input(),i("design:type",String)],Button.prototype,"label",null),Button=r([o.Directive({selector:"[pButton]",host:{"[class.ui-state-hover]":"hover&&!isDisabled()","[class.ui-state-focus]":"focus","[class.ui-state-active]":"active","[class.ui-state-disabled]":"isDisabled()"},providers:[s.DomHandler]}),i("design:paramtypes",[o.ElementRef,s.DomHandler])],Button)}();t.Button=l;var c=function(){function ButtonModule(){}return ButtonModule=r([o.NgModule({imports:[a.CommonModule],exports:[l],declarations:[l]}),i("design:paramtypes",[])],ButtonModule)}();t.ButtonModule=c},function(e,t,n){"use strict";var r=n(1),i=n(506);r.Observable.prototype.map=i.map},function(e,t,n){"use strict";var r=n(80);t.of=r.ArrayObservable.of},function(e,t,n){"use strict";var r=n(52),i=r.root.Symbol;if("function"==typeof i)i.iterator?t.$$iterator=i.iterator:"function"==typeof i.for&&(t.$$iterator=i.for("iterator"));else if(r.root.Set&&"function"==typeof(new r.root.Set)["@@iterator"])t.$$iterator="@@iterator";else if(r.root.Map)for(var o=Object.getOwnPropertyNames(r.root.Map.prototype),s=0;s=0;a--)(i=e[a])&&(s=(o<3?i(s):o>3?i(t,n,s):i(t,n))||s);return o>3&&s&&Object.defineProperty(t,n,s),s},c=this&&this.__metadata||function(e,t){if("object"==typeof Reflect&&"function"==typeof Reflect.metadata)return Reflect.metadata(e,t)},u=function(){function TabletStatusService(e){this.http=e}return TabletStatusService.prototype.getTabletStats=function(e,t,n,i){var s=this,a=new r.URLSearchParams;return a.set("keyspace",e),a.set("cell",t),a.set("type",n),a.set("metric",i),o.Observable.interval(1e3).startWith(0).switchMap(function(){return s.http.get("../api/tablet_statuses/",{search:a}).map(function(e){return e.json()})})},TabletStatusService.prototype.getTabletHealth=function(e,t){return this.http.get("../api/tablet_health/"+e+"/"+t).map(function(e){return e.json()})},TabletStatusService=l([n.i(i.Injectable)(),c("design:paramtypes",["function"==typeof(e="undefined"!=typeof r.Http&&r.Http)&&e||Object])],TabletStatusService);var e}()},function(e,t,n){"use strict";var r=n(0),i=(n.n(r),n(44)),o=(n.n(i),n(216));n.d(t,"a",function(){return l});var s=this&&this.__decorate||function(e,t,n,r){var i,o=arguments.length,s=o<3?t:null===r?r=Object.getOwnPropertyDescriptor(t,n):r;if("object"==typeof Reflect&&"function"==typeof Reflect.decorate)s=Reflect.decorate(e,t,n,r);else for(var a=e.length-1;a>=0;a--)(i=e[a])&&(s=(o<3?i(s):o>3?i(t,n,s):i(t,n))||s);return o>3&&s&&Object.defineProperty(t,n,s),s},a=this&&this.__metadata||function(e,t){if("object"==typeof Reflect&&"function"==typeof Reflect.metadata)return Reflect.metadata(e,t)},l=function(){function TabletService(e){this.http=e,this.tabletsUrl="/api/tablets/"}return TabletService.prototype.getTabletList=function(e){return this.sendUrlPostRequest(this.tabletsUrl,"shard="+e)},TabletService.prototype.getTablet=function(e){return this.http.get(this.tabletsUrl+e).map(function(e){var t=e.json();return t.type=o.a.VT_TABLET_TYPES[t.type],t.label=t.type+"("+t.alias.uid+")",t.cell=t.alias.cell,t.uid=t.alias.uid,t.alias=t.cell+"-"+t.uid,t})},TabletService.prototype.sendUrlPostRequest=function(e,t){var n=new i.Headers({"Content-Type":"application/x-www-form-urlencoded"}),r=new i.RequestOptions({headers:n});return this.http.post(e,t,r).map(function(e){return e.json()})},TabletService=s([n.i(r.Injectable)(),a("design:paramtypes",["function"==typeof(e="undefined"!=typeof i.Http&&i.Http)&&e||Object])],TabletService);var e}()},function(e,t,n){"use strict";n.d(t,"a",function(){return o}),n.d(t,"b",function(){return s}),n.d(t,"c",function(){return a});var r=this&&this.__extends||function(e,t){function __(){this.constructor=e}for(var n in t)t.hasOwnProperty(n)&&(e[n]=t[n]);e.prototype=null===t?Object.create(t):(__.prototype=t.prototype,new __)},i=function(){function Flag(e,t,n,r,i,o,s){void 0===i&&(i=""),void 0===o&&(o=""),void 0===s&&(s=!0),this.blockOnEmptyList=[],this.blockOnFilledList=[],this.positional=!1,this.position=e,this.type=t,this.id=n,this.name=r,this.description=i,this.value=o,this.show=s}return Flag.prototype.setBlockOnEmptyList=function(e){this.blockOnEmptyList=e},Flag.prototype.getBlockOnEmptyList=function(){return this.blockOnEmptyList},Flag.prototype.setBlockOnFilledList=function(e){this.blockOnFilledList=e},Flag.prototype.getBlockOnFilledList=function(){return this.blockOnFilledList},Flag.prototype.setDisplayOn=function(e,t){this.displayOnFlag=e,this.displayOnValue=t},Flag.prototype.getDisplayOnFlag=function(){return this.displayOnFlag},Flag.prototype.getDisplayOnValue=function(){return this.displayOnValue},Flag.prototype.getStrValue=function(){return this.value},Flag.prototype.getValue=function(){return this.value},Flag.prototype.setValue=function(e){this.value=e},Flag.prototype.isEmpty=function(){return""===this.value},Flag.prototype.isFilled=function(){return""!==this.value},Flag.prototype.getArgs=function(){return this.getValue()!==!1&&""!==this.getStrValue()&&this.positional?void 0!==this.namedPositional?this.getValue()===!0?["-"+this.namedPositional]:["-"+this.namedPositional+"="+this.getStrValue()]:[this.getStrValue()]:[]},Flag.prototype.getFlags=function(){return this.getValue()===!1||""===this.getStrValue()||this.positional?[]:this.getValue()===!0?["-"+this.id]:["-"+this.id+"="+this.getStrValue()]},Flag}(),o=function(e){function InputFlag(t,n,r,i,o,s){void 0===i&&(i=""),void 0===o&&(o=""),void 0===s&&(s=!0),e.call(this,t,"input",n,r,i,o,s),this.value=o}return r(InputFlag,e),InputFlag}(i),s=function(e){function CheckBoxFlag(t,n,r,i,o,s){void 0===i&&(i=""),void 0===o&&(o=!1),void 0===s&&(s=!0),e.call(this,t,"checkBox",n,r,i,"",s),this.value=o}return r(CheckBoxFlag,e),CheckBoxFlag.prototype.getStrValue=function(){return this.value?"true":"false"},CheckBoxFlag}(i),a=function(e){function DropDownFlag(t,n,r,i,o,s){void 0===i&&(i=""),void 0===o&&(o=""),void 0===s&&(s=!0),e.call(this,t,"dropDown",n,r,i,o,s)}return r(DropDownFlag,e),DropDownFlag.prototype.setOptions=function(e){this.options=e},DropDownFlag.prototype.getOptions=function(){return this.options},DropDownFlag}(i)},function(e,t,n){"use strict";n.d(t,"a",function(){return r});var r=function(){function PrepareResponse(e,t,n){void 0===t&&(t={}),void 0===n&&(n=""),this.success=e,this.flags=t,this.message=n}return PrepareResponse}()},function(e,t,n){"use strict";var r=n(0),i=n(53);t.CHECKBOX_VALUE_ACCESSOR={provide:i.NG_VALUE_ACCESSOR,useExisting:r.forwardRef(function(){return o}),multi:!0};var o=function(){function CheckboxControlValueAccessor(e,t){this._renderer=e,this._elementRef=t,this.onChange=function(e){},this.onTouched=function(){}}return CheckboxControlValueAccessor.prototype.writeValue=function(e){this._renderer.setElementProperty(this._elementRef.nativeElement,"checked",e)},CheckboxControlValueAccessor.prototype.registerOnChange=function(e){this.onChange=e},CheckboxControlValueAccessor.prototype.registerOnTouched=function(e){this.onTouched=e},CheckboxControlValueAccessor.decorators=[{type:r.Directive,args:[{selector:"input[type=checkbox][ngControl],input[type=checkbox][ngFormControl],input[type=checkbox][ngModel]",host:{"(change)":"onChange($event.target.checked)","(blur)":"onTouched()"},providers:[t.CHECKBOX_VALUE_ACCESSOR]}]}],CheckboxControlValueAccessor.ctorParameters=[{type:r.Renderer},{type:r.ElementRef}],CheckboxControlValueAccessor}();t.CheckboxControlValueAccessor=o},function(e,t,n){"use strict";var r=n(0),i=n(7),o=n(53);t.DEFAULT_VALUE_ACCESSOR={provide:o.NG_VALUE_ACCESSOR,useExisting:r.forwardRef(function(){return s}),multi:!0};var s=function(){function DefaultValueAccessor(e,t){this._renderer=e,this._elementRef=t,this.onChange=function(e){},this.onTouched=function(){}}return DefaultValueAccessor.prototype.writeValue=function(e){var t=i.isBlank(e)?"":e;this._renderer.setElementProperty(this._elementRef.nativeElement,"value",t)},DefaultValueAccessor.prototype.registerOnChange=function(e){this.onChange=e},DefaultValueAccessor.prototype.registerOnTouched=function(e){this.onTouched=e},DefaultValueAccessor.decorators=[{type:r.Directive,args:[{selector:"input:not([type=checkbox])[ngControl],textarea[ngControl],input:not([type=checkbox])[ngFormControl],textarea[ngFormControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]",host:{"(input)":"onChange($event.target.value)","(blur)":"onTouched()"},providers:[t.DEFAULT_VALUE_ACCESSOR]}]}],DefaultValueAccessor.ctorParameters=[{type:r.Renderer},{type:r.ElementRef}],DefaultValueAccessor}();t.DefaultValueAccessor=s},function(e,t,n){"use strict";var r=n(0),i=n(34),o=n(7),s=n(53),a=n(87);t.RADIO_VALUE_ACCESSOR={provide:s.NG_VALUE_ACCESSOR,useExisting:r.forwardRef(function(){return u}),multi:!0};var l=function(){function RadioControlRegistry(){this._accessors=[]}return RadioControlRegistry.prototype.add=function(e,t){this._accessors.push([e,t])},RadioControlRegistry.prototype.remove=function(e){for(var t=-1,n=0;n=this.length?i.$EOF:o.StringWrapper.charCodeAt(this.input,this.index)},_Scanner.prototype.scanToken=function(){for(var e=this.input,t=this.length,n=this.peek,r=this.index;n<=i.$SPACE;){if(++r>=t){n=i.$EOF;break}n=o.StringWrapper.charCodeAt(e,r)}if(this.peek=n,this.index=r,r>=t)return null;if(isIdentifierStart(n))return this.scanIdentifier();if(i.isDigit(n))return this.scanNumber(r);var s=r;switch(n){case i.$PERIOD:return this.advance(),i.isDigit(this.peek)?this.scanNumber(s):newCharacterToken(s,i.$PERIOD);case i.$LPAREN:case i.$RPAREN:case i.$LBRACE:case i.$RBRACE:case i.$LBRACKET:case i.$RBRACKET:case i.$COMMA:case i.$COLON:case i.$SEMICOLON:return this.scanCharacter(s,n);case i.$SQ:case i.$DQ:return this.scanString();case i.$HASH:case i.$PLUS:case i.$MINUS:case i.$STAR:case i.$SLASH:case i.$PERCENT:case i.$CARET:return this.scanOperator(s,o.StringWrapper.fromCharCode(n));case i.$QUESTION:return this.scanComplexOperator(s,"?",i.$PERIOD,".");case i.$LT:case i.$GT:return this.scanComplexOperator(s,o.StringWrapper.fromCharCode(n),i.$EQ,"=");case i.$BANG:case i.$EQ:return this.scanComplexOperator(s,o.StringWrapper.fromCharCode(n),i.$EQ,"=",i.$EQ,"=");case i.$AMPERSAND:return this.scanComplexOperator(s,"&",i.$AMPERSAND,"&");case i.$BAR:return this.scanComplexOperator(s,"|",i.$BAR,"|");case i.$NBSP:for(;i.isWhitespace(this.peek);)this.advance();return this.scanToken()}return this.advance(),this.error("Unexpected character ["+o.StringWrapper.fromCharCode(n)+"]",0)},_Scanner.prototype.scanCharacter=function(e,t){return this.advance(),newCharacterToken(e,t)},_Scanner.prototype.scanOperator=function(e,t){return this.advance(),newOperatorToken(e,t)},_Scanner.prototype.scanComplexOperator=function(e,t,n,r,i,s){this.advance();var a=t;return this.peek==n&&(this.advance(),a+=r),o.isPresent(i)&&this.peek==i&&(this.advance(),a+=s),newOperatorToken(e,a)},_Scanner.prototype.scanIdentifier=function(){var e=this.index;for(this.advance();isIdentifierPart(this.peek);)this.advance();var t=this.input.substring(e,this.index);return a.indexOf(t)>-1?newKeywordToken(e,t):newIdentifierToken(e,t)},_Scanner.prototype.scanNumber=function(e){var t=this.index===e;for(this.advance();;){if(i.isDigit(this.peek));else if(this.peek==i.$PERIOD)t=!1;else{if(!isExponentStart(this.peek))break;if(this.advance(),isExponentSign(this.peek)&&this.advance(),!i.isDigit(this.peek))return this.error("Invalid exponent",-1);t=!1}this.advance()}var n=this.input.substring(e,this.index),r=t?o.NumberWrapper.parseIntAutoRadix(n):o.NumberWrapper.parseFloat(n);return newNumberToken(e,r)},_Scanner.prototype.scanString=function(){var e=this.index,t=this.peek;this.advance();for(var n,r=this.index,s=this.input;this.peek!=t;)if(this.peek==i.$BACKSLASH){null==n&&(n=new o.StringJoiner),n.add(s.substring(r,this.index)),this.advance();var a;if(this.peek==i.$u){var l=s.substring(this.index+1,this.index+5);try{a=o.NumberWrapper.parseInt(l,16)}catch(c){return this.error("Invalid unicode escape [\\u"+l+"]",0)}for(var u=0;u<5;u++)this.advance()}else a=unescape(this.peek),this.advance();n.add(o.StringWrapper.fromCharCode(a)),r=this.index}else{if(this.peek==i.$EOF)return this.error("Unterminated quote",0);this.advance()}var p=s.substring(r,this.index);this.advance();var d=p;return null!=n&&(n.add(p),d=n.toString()),newStringToken(e,d)},_Scanner.prototype.error=function(e,t){var n=this.index+t;return newErrorToken(n,"Lexer Error: "+e+" at column "+n+" in expression ["+this.input+"]")},_Scanner}();t.isIdentifier=isIdentifier,t.isQuote=isQuote},function(e,t,n){"use strict";function _createInterpolateRegExp(e){var t=o.escapeRegExp(e.start)+"([\\s\\S]*?)"+o.escapeRegExp(e.end);return new RegExp(t,"g")}var r=n(0),i=n(236),o=n(5),s=n(71),a=n(239),l=n(154),c=function(){function SplitInterpolation(e,t){this.strings=e,this.expressions=t}return SplitInterpolation}();t.SplitInterpolation=c;var u=function(){function TemplateBindingParseResult(e,t,n){this.templateBindings=e,this.warnings=t,this.errors=n}return TemplateBindingParseResult}();t.TemplateBindingParseResult=u;var p=function(){function Parser(e){this._lexer=e,this.errors=[]}return Parser.prototype.parseAction=function(e,t,n){void 0===n&&(n=s.DEFAULT_INTERPOLATION_CONFIG),this._checkNoInterpolation(e,t,n);var r=this._lexer.tokenize(this._stripComments(e)),i=new d(e,t,r,(!0),this.errors).parseChain();return new a.ASTWithSource(i,e,t,this.errors)},Parser.prototype.parseBinding=function(e,t,n){void 0===n&&(n=s.DEFAULT_INTERPOLATION_CONFIG);var r=this._parseBindingAst(e,t,n);return new a.ASTWithSource(r,e,t,this.errors)},Parser.prototype.parseSimpleBinding=function(e,t,n){void 0===n&&(n=s.DEFAULT_INTERPOLATION_CONFIG);var r=this._parseBindingAst(e,t,n);return h.check(r)||this._reportError("Host binding expression can only contain field access and constants",e,t),new a.ASTWithSource(r,e,t,this.errors)},Parser.prototype._reportError=function(e,t,n,r){this.errors.push(new a.ParserError(e,t,n,r))},Parser.prototype._parseBindingAst=function(e,t,n){var r=this._parseQuote(e,t);if(o.isPresent(r))return r;this._checkNoInterpolation(e,t,n);var i=this._lexer.tokenize(this._stripComments(e));return new d(e,t,i,(!1),this.errors).parseChain()},Parser.prototype._parseQuote=function(e,t){if(o.isBlank(e))return null;var n=e.indexOf(":");if(n==-1)return null;var r=e.substring(0,n).trim();if(!l.isIdentifier(r))return null;var i=e.substring(n+1);return new a.Quote(new a.ParseSpan(0,e.length),r,i,t)},Parser.prototype.parseTemplateBindings=function(e,t){var n=this._lexer.tokenize(e);return new d(e,t,n,(!1),this.errors).parseTemplateBindings()},Parser.prototype.parseInterpolation=function(e,t,n){void 0===n&&(n=s.DEFAULT_INTERPOLATION_CONFIG);var r=this.splitInterpolation(e,t,n);if(null==r)return null;for(var i=[],l=0;l0?l.push(p):this._reportError("Blank expressions are not allowed in interpolated strings",e,"at column "+this._findInterpolationErrorColumn(i,u,n)+" in",t)}return new c(a,l)},Parser.prototype.wrapLiteralPrimitive=function(e,t){return new a.ASTWithSource(new a.LiteralPrimitive(new a.ParseSpan(0,o.isBlank(e)?0:e.length),e),e,t,this.errors)},Parser.prototype._stripComments=function(e){var t=this._commentStart(e);return o.isPresent(t)?e.substring(0,t).trim():e},Parser.prototype._commentStart=function(e){for(var t=null,n=0;n1&&this._reportError("Got interpolation ("+n.start+n.end+") where expression was expected",e,"at column "+this._findInterpolationErrorColumn(i,1,n)+" in",t)},Parser.prototype._findInterpolationErrorColumn=function(e,t,n){for(var r="",i=0;i":case"<=":case">=":this.advance();var n=this.parseAdditive();e=new a.Binary(this.span(e.span.start),t,e,n);continue}break}return e},_ParseAST.prototype.parseAdditive=function(){for(var e=this.parseMultiplicative();this.next.type==l.TokenType.Operator;){var t=this.next.strValue;switch(t){case"+":case"-":this.advance();var n=this.parseMultiplicative();e=new a.Binary(this.span(e.span.start),t,e,n);continue}break}return e},_ParseAST.prototype.parseMultiplicative=function(){for(var e=this.parsePrefix();this.next.type==l.TokenType.Operator;){var t=this.next.strValue;switch(t){case"*":case"%":case"/":this.advance();var n=this.parsePrefix();e=new a.Binary(this.span(e.span.start),t,e,n);continue}break}return e},_ParseAST.prototype.parsePrefix=function(){if(this.next.type==l.TokenType.Operator){var e=this.inputIndex,t=this.next.strValue,n=void 0;switch(t){case"+":return this.advance(),this.parsePrefix();case"-":return this.advance(),n=this.parsePrefix(),new a.Binary(this.span(e),t,new a.LiteralPrimitive(new a.ParseSpan(e,e),0),n);case"!":return this.advance(),n=this.parsePrefix(),new a.PrefixNot(this.span(e),n)}}return this.parseCallChain()},_ParseAST.prototype.parseCallChain=function(){for(var e=this.parsePrimary();;)if(this.optionalCharacter(i.$PERIOD))e=this.parseAccessMemberOrMethodCall(e,!1);else if(this.optionalOperator("?."))e=this.parseAccessMemberOrMethodCall(e,!0);else if(this.optionalCharacter(i.$LBRACKET)){this.rbracketsExpected++;var t=this.parsePipe();if(this.rbracketsExpected--,this.expectCharacter(i.$RBRACKET),this.optionalOperator("=")){var n=this.parseConditional();e=new a.KeyedWrite(this.span(e.span.start),e,t,n)}else e=new a.KeyedRead(this.span(e.span.start),e,t)}else{if(!this.optionalCharacter(i.$LPAREN))return e;this.rparensExpected++;var r=this.parseCallArguments();this.rparensExpected--,this.expectCharacter(i.$RPAREN),e=new a.FunctionCall(this.span(e.span.start),e,r)}},_ParseAST.prototype.parsePrimary=function(){var e=this.inputIndex;if(this.optionalCharacter(i.$LPAREN)){this.rparensExpected++;var t=this.parsePipe();return this.rparensExpected--,this.expectCharacter(i.$RPAREN),t}if(this.next.isKeywordNull())return this.advance(),new a.LiteralPrimitive(this.span(e),null);if(this.next.isKeywordUndefined())return this.advance(),new a.LiteralPrimitive(this.span(e),(void 0));if(this.next.isKeywordTrue())return this.advance(),new a.LiteralPrimitive(this.span(e),(!0));if(this.next.isKeywordFalse())return this.advance(),new a.LiteralPrimitive(this.span(e),(!1));if(this.next.isKeywordThis())return this.advance(),new a.ImplicitReceiver(this.span(e));if(this.optionalCharacter(i.$LBRACKET)){this.rbracketsExpected++;var n=this.parseExpressionList(i.$RBRACKET);return this.rbracketsExpected--,this.expectCharacter(i.$RBRACKET),new a.LiteralArray(this.span(e),n)}if(this.next.isCharacter(i.$LBRACE))return this.parseLiteralMap();if(this.next.isIdentifier())return this.parseAccessMemberOrMethodCall(new a.ImplicitReceiver(this.span(e)),!1);if(this.next.isNumber()){var r=this.next.toNumber();return this.advance(),new a.LiteralPrimitive(this.span(e),r)}if(this.next.isString()){var o=this.next.toString();return this.advance(),new a.LiteralPrimitive(this.span(e),o)}return this.index>=this.tokens.length?(this.error("Unexpected end of expression: "+this.input),new a.EmptyExpr(this.span(e))):(this.error("Unexpected token "+this.next),new a.EmptyExpr(this.span(e)))},_ParseAST.prototype.parseExpressionList=function(e){var t=[];if(!this.next.isCharacter(e))do t.push(this.parsePipe());while(this.optionalCharacter(i.$COMMA));return t},_ParseAST.prototype.parseLiteralMap=function(){var e=[],t=[],n=this.inputIndex;if(this.expectCharacter(i.$LBRACE),!this.optionalCharacter(i.$RBRACE)){this.rbracesExpected++;do{var r=this.expectIdentifierOrKeywordOrString();e.push(r),this.expectCharacter(i.$COLON),t.push(this.parsePipe())}while(this.optionalCharacter(i.$COMMA));this.rbracesExpected--,this.expectCharacter(i.$RBRACE)}return new a.LiteralMap(this.span(n),e,t)},_ParseAST.prototype.parseAccessMemberOrMethodCall=function(e,t){void 0===t&&(t=!1);var n=e.span.start,r=this.expectIdentifierOrKeyword();if(this.optionalCharacter(i.$LPAREN)){this.rparensExpected++;var o=this.parseCallArguments();this.expectCharacter(i.$RPAREN),this.rparensExpected--;var s=this.span(n);return t?new a.SafeMethodCall(s,e,r,o):new a.MethodCall(s,e,r,o)}if(t)return this.optionalOperator("=")?(this.error("The '?.' operator cannot be used in the assignment"),new a.EmptyExpr(this.span(n))):new a.SafePropertyRead(this.span(n),e,r);if(this.optionalOperator("=")){if(!this.parseAction)return this.error("Bindings cannot contain assignments"),new a.EmptyExpr(this.span(n));var l=this.parseConditional();return new a.PropertyWrite(this.span(n),e,r,l)}return new a.PropertyRead(this.span(n),e,r)},_ParseAST.prototype.parseCallArguments=function(){if(this.next.isCharacter(i.$RPAREN))return[];var e=[];do e.push(this.parsePipe());while(this.optionalCharacter(i.$COMMA));return e},_ParseAST.prototype.expectTemplateBindingKey=function(){var e="",t=!1;do e+=this.expectIdentifierOrKeywordOrString(),t=this.optionalOperator("-"),t&&(e+="-");while(t);return e.toString()},_ParseAST.prototype.parseTemplateBindings=function(){for(var e=[],t=null,n=[];this.index0&&e[e.length-1]===t}var r=this&&this.__extends||function(e,t){function __(){this.constructor=e}for(var n in t)t.hasOwnProperty(n)&&(e[n]=t[n]);e.prototype=null===t?Object.create(t):(__.prototype=t.prototype,new __)},i=n(10),o=n(5),s=n(60),a=n(88),l=n(71),c=n(594),u=n(103),p=function(e){function TreeError(t,n,r){e.call(this,n,r),this.elementName=t}return r(TreeError,e),TreeError.create=function(e,t,n){return new TreeError(e,t,n)},TreeError}(s.ParseError);t.TreeError=p;var d=function(){function ParseTreeResult(e,t){this.rootNodes=e,this.errors=t}return ParseTreeResult}();t.ParseTreeResult=d;var h=function(){function Parser(e){this._getTagDefinition=e}return Parser.prototype.parse=function(e,t,n,r){void 0===n&&(n=!1),void 0===r&&(r=l.DEFAULT_INTERPOLATION_CONFIG);var i=c.tokenize(e,t,this._getTagDefinition,n,r),o=new f(i.tokens,this._getTagDefinition).build();return new d(o.rootNodes,i.errors.concat(o.errors))},Parser}();t.Parser=h;var f=function(){function _TreeBuilder(e,t){this.tokens=e,this.getTagDefinition=t,this._index=-1,this._rootNodes=[],this._errors=[],this._elementStack=[],this._advance()}return _TreeBuilder.prototype.build=function(){for(;this._peek.type!==c.TokenType.EOF;)this._peek.type===c.TokenType.TAG_OPEN_START?this._consumeStartTag(this._advance()):this._peek.type===c.TokenType.TAG_CLOSE?this._consumeEndTag(this._advance()):this._peek.type===c.TokenType.CDATA_START?(this._closeVoidElement(),this._consumeCdata(this._advance())):this._peek.type===c.TokenType.COMMENT_START?(this._closeVoidElement(),this._consumeComment(this._advance())):this._peek.type===c.TokenType.TEXT||this._peek.type===c.TokenType.RAW_TEXT||this._peek.type===c.TokenType.ESCAPABLE_RAW_TEXT?(this._closeVoidElement(),this._consumeText(this._advance())):this._peek.type===c.TokenType.EXPANSION_FORM_START?this._consumeExpansion(this._advance()):this._advance();return new d(this._rootNodes,this._errors)},_TreeBuilder.prototype._advance=function(){var e=this._peek;return this._index0)return this._errors=this._errors.concat(i.errors),null;var l=new s.ParseSourceSpan(e.sourceSpan.start,r.sourceSpan.end),u=new s.ParseSourceSpan(t.sourceSpan.start,r.sourceSpan.end);return new a.ExpansionCase(e.parts[0],i.rootNodes,l,e.sourceSpan,u)},_TreeBuilder.prototype._collectExpansionExpTokens=function(e){for(var t=[],n=[c.TokenType.EXPANSION_CASE_EXP_START];;){if(this._peek.type!==c.TokenType.EXPANSION_FORM_START&&this._peek.type!==c.TokenType.EXPANSION_CASE_EXP_START||n.push(this._peek.type),this._peek.type===c.TokenType.EXPANSION_CASE_EXP_END){if(!lastOnStack(n,c.TokenType.EXPANSION_CASE_EXP_START))return this._errors.push(p.create(null,e.sourceSpan,"Invalid ICU message. Missing '}'.")),null;if(n.pop(),0==n.length)return t}if(this._peek.type===c.TokenType.EXPANSION_FORM_END){if(!lastOnStack(n,c.TokenType.EXPANSION_FORM_START))return this._errors.push(p.create(null,e.sourceSpan,"Invalid ICU message. Missing '}'.")),null;n.pop()}if(this._peek.type===c.TokenType.EOF)return this._errors.push(p.create(null,e.sourceSpan,"Invalid ICU message. Missing '}'.")),null;t.push(this._advance())}},_TreeBuilder.prototype._consumeText=function(e){var t=e.parts[0];if(t.length>0&&"\n"==t[0]){var n=this._getParentElement();o.isPresent(n)&&0==n.children.length&&this.getTagDefinition(n.name).ignoreFirstLf&&(t=t.substring(1))}t.length>0&&this._addToParent(new a.Text(t,e.sourceSpan))},_TreeBuilder.prototype._closeVoidElement=function(){if(this._elementStack.length>0){var e=i.ListWrapper.last(this._elementStack);this.getTagDefinition(e.name).isVoid&&this._elementStack.pop()}},_TreeBuilder.prototype._consumeStartTag=function(e){for(var t=e.parts[0],n=e.parts[1],r=[];this._peek.type===c.TokenType.ATTR_NAME;)r.push(this._consumeAttr(this._advance()));var i=this._getElementFullName(t,n,this._getParentElement()),o=!1;if(this._peek.type===c.TokenType.TAG_OPEN_END_VOID){this._advance(),o=!0;var l=this.getTagDefinition(i);l.canSelfClose||null!==u.getNsPrefix(i)||l.isVoid||this._errors.push(p.create(i,e.sourceSpan,'Only void and foreign elements can be self closed "'+e.parts[1]+'"'))}else this._peek.type===c.TokenType.TAG_OPEN_END&&(this._advance(),o=!1);var d=this._peek.sourceSpan.start,h=new s.ParseSourceSpan(e.sourceSpan.start,d),f=new a.Element(i,r,[],h,h,null);this._pushElement(f),o&&(this._popElement(i),f.endSourceSpan=h)},_TreeBuilder.prototype._pushElement=function(e){if(this._elementStack.length>0){var t=i.ListWrapper.last(this._elementStack);this.getTagDefinition(t.name).isClosedByChild(e.name)&&this._elementStack.pop()}var n=this.getTagDefinition(e.name),r=this._getParentElementSkippingContainers(),s=r.parent,l=r.container;if(o.isPresent(s)&&n.requireExtraParent(s.name)){var c=new a.Element(n.parentToAdd,[],[],e.sourceSpan,e.startSourceSpan,e.endSourceSpan);this._insertBeforeContainer(s,l,c)}this._addToParent(e),this._elementStack.push(e)},_TreeBuilder.prototype._consumeEndTag=function(e){var t=this._getElementFullName(e.parts[0],e.parts[1],this._getParentElement());this._getParentElement()&&(this._getParentElement().endSourceSpan=e.sourceSpan),this.getTagDefinition(t).isVoid?this._errors.push(p.create(t,e.sourceSpan,'Void elements do not have end tags "'+e.parts[1]+'"')):this._popElement(t)||this._errors.push(p.create(t,e.sourceSpan,'Unexpected closing tag "'+e.parts[1]+'"'))},_TreeBuilder.prototype._popElement=function(e){for(var t=this._elementStack.length-1;t>=0;t--){var n=this._elementStack[t];if(n.name==e)return i.ListWrapper.splice(this._elementStack,t,this._elementStack.length-t),!0;if(!this.getTagDefinition(n.name).closedByParent)return!1}return!1},_TreeBuilder.prototype._consumeAttr=function(e){var t=u.mergeNsAndName(e.parts[0],e.parts[1]),n=e.sourceSpan.end,r="";if(this._peek.type===c.TokenType.ATTR_VALUE){var i=this._advance();r=i.parts[0],n=i.sourceSpan.end}return new a.Attribute(t,r,new s.ParseSourceSpan(e.sourceSpan.start,n))},_TreeBuilder.prototype._getParentElement=function(){return this._elementStack.length>0?i.ListWrapper.last(this._elementStack):null},_TreeBuilder.prototype._getParentElementSkippingContainers=function(){for(var e=null,t=this._elementStack.length-1;t>=0;t--){if("ng-container"!==this._elementStack[t].name)return{parent:this._elementStack[t],container:e};e=this._elementStack[t]}return{parent:i.ListWrapper.last(this._elementStack),container:e}},_TreeBuilder.prototype._addToParent=function(e){var t=this._getParentElement();o.isPresent(t)?t.children.push(e):this._rootNodes.push(e)},_TreeBuilder.prototype._insertBeforeContainer=function(e,t,n){if(t){if(e){var r=e.children.indexOf(t);e.children[r]=n}else this._rootNodes.push(n);n.children.push(t),this._elementStack.splice(this._elementStack.indexOf(t),0,n)}else this._addToParent(n),this._elementStack.push(n)},_TreeBuilder.prototype._getElementFullName=function(e,t,n){return o.isBlank(e)&&(e=this.getTagDefinition(t).implicitNamespacePrefix,o.isBlank(e)&&o.isPresent(n)&&(e=u.getNsPrefix(n.name))),u.mergeNsAndName(e,t)},_TreeBuilder}()},function(e,t,n){"use strict";function splitClasses(e){return e.trim().split(/\s+/g)}function createElementCssSelector(e,t){var n=new S.CssSelector,r=v.splitNsName(e)[1];n.setElement(r);for(var i=0;i0&&this._console.warn("Template parse warnings:\n"+a.join("\n")),l.length>0){var c=l.join("\n");throw new u.BaseException("Template parse errors:\n"+c)}return s.templateAst},TemplateParser.prototype.tryParse=function(e,t,n,r,i,o){var a;e.template&&(a=y.InterpolationConfig.fromArray(e.template.interpolation));var l,c=this._htmlParser.parse(t,o,!0,a),u=c.errors;if(0==u.length){var d=m.expandNodes(c.rootNodes);u.push.apply(u,d.errors),c=new f.ParseTreeResult(d.nodes,u)}if(c.rootNodes.length>0){var v=s.removeIdentifierDuplicates(n),g=s.removeIdentifierDuplicates(r),_=new b.ProviderViewContext(e,c.rootNodes[0].sourceSpan),S=new j(_,v,g,i,this._exprParser,this._schemaRegistry);l=h.visitAll(S,c.rootNodes,z),u.push.apply(u,S.errors.concat(_.errors))}else l=[];return this._assertNoReferenceDuplicationOnTemplate(l,u),u.length>0?new L(l,u):(p.isPresent(this.transforms)&&this.transforms.forEach(function(e){l=E.templateVisitAll(e,l)}),new L(l,u))},TemplateParser.prototype._assertNoReferenceDuplicationOnTemplate=function(e,t){var n=[];e.filter(function(e){return!!e.references}).forEach(function(e){return e.references.forEach(function(e){var r=e.name;if(n.indexOf(r)<0)n.push(r);else{var i=new V('Reference "#'+r+'" is defined several times',e.sourceSpan,g.ParseErrorLevel.FATAL);t.push(i)}})})},TemplateParser.decorators=[{type:i.Injectable}],TemplateParser.ctorParameters=[{type:l.Parser},{type:_.ElementSchemaRegistry},{type:f.HtmlParser},{type:o.Console},{type:Array,decorators:[{type:i.Optional},{type:i.Inject,args:[t.TEMPLATE_TRANSFORMS]}]}],TemplateParser}();t.TemplateParser=F;var j=function(){function TemplateParseVisitor(e,t,n,r,i,o){var s=this;this.providerViewContext=e,this._schemas=r,this._exprParser=i,this._schemaRegistry=o,this.errors=[],this.directivesIndex=new Map,this.ngContentCount=0,this.selectorMatcher=new S.SelectorMatcher;var a=e.component.template;p.isPresent(a)&&p.isPresent(a.interpolation)&&(this._interpolationConfig={start:a.interpolation[0],end:a.interpolation[1]}),c.ListWrapper.forEachWithIndex(t,function(e,t){var n=S.CssSelector.parse(e.selector);s.selectorMatcher.addSelectables(n,e),s.directivesIndex.set(e,t)}),this.pipesByName=new Map,n.forEach(function(e){return s.pipesByName.set(e.name,e)})}return TemplateParseVisitor.prototype._reportError=function(e,t,n){void 0===n&&(n=g.ParseErrorLevel.FATAL),this.errors.push(new V(e,t,n))},TemplateParseVisitor.prototype._reportParserErors=function(e,t){for(var n=0,r=e;no.MAX_INTERPOLATION_VALUES)throw new u.BaseException("Only support at most "+o.MAX_INTERPOLATION_VALUES+" interpolation values!");return r}catch(i){return this._reportError(""+i,t),this._exprParser.wrapLiteralPrimitive("ERROR",n)}},TemplateParseVisitor.prototype._parseAction=function(e,t){var n=t.start.toString();try{var r=this._exprParser.parseAction(e,n,this._interpolationConfig);return r&&this._reportParserErors(r.errors,t),!r||r.ast instanceof a.EmptyExpr?(this._reportError("Empty expressions are not allowed",t),this._exprParser.wrapLiteralPrimitive("ERROR",n)):(this._checkPipes(r,t),r)}catch(i){return this._reportError(""+i,t),this._exprParser.wrapLiteralPrimitive("ERROR",n)}},TemplateParseVisitor.prototype._parseBinding=function(e,t){var n=t.start.toString();try{var r=this._exprParser.parseBinding(e,n,this._interpolationConfig);return r&&this._reportParserErors(r.errors,t),this._checkPipes(r,t),r}catch(i){return this._reportError(""+i,t),this._exprParser.wrapLiteralPrimitive("ERROR",n)}},TemplateParseVisitor.prototype._parseTemplateBindings=function(e,t){var n=this,r=t.start.toString();try{var i=this._exprParser.parseTemplateBindings(e,r);return this._reportParserErors(i.errors,t),i.templateBindings.forEach(function(e){p.isPresent(e.expression)&&n._checkPipes(e.expression,t)}),i.warnings.forEach(function(e){n._reportError(e,t,g.ParseErrorLevel.WARNING)}),i.templateBindings}catch(o){return this._reportError(""+o,t),[]}},TemplateParseVisitor.prototype._checkPipes=function(e,t){var n=this;if(p.isPresent(e)){var r=new q;e.visit(r),r.pipes.forEach(function(e){n.pipesByName.has(e)||n._reportError("The pipe '"+e+"' could not be found",t)})}},TemplateParseVisitor.prototype.visitExpansion=function(e,t){return null},TemplateParseVisitor.prototype.visitExpansionCase=function(e,t){return null},TemplateParseVisitor.prototype.visitText=function(e,t){var n=t.findNgContentIndex(N),r=this._parseInterpolation(e.value,e.sourceSpan);return p.isPresent(r)?new E.BoundTextAst(r,n,e.sourceSpan):new E.TextAst(e.value,n,e.sourceSpan)},TemplateParseVisitor.prototype.visitAttribute=function(e,t){return new E.AttrAst(e.name,e.value,e.sourceSpan)},TemplateParseVisitor.prototype.visitComment=function(e,t){return null},TemplateParseVisitor.prototype.visitElement=function(e,t){var n=this,r=e.name,i=T.preparseElement(e);if(i.type===T.PreparsedElementType.SCRIPT||i.type===T.PreparsedElementType.STYLE)return null;if(i.type===T.PreparsedElementType.STYLESHEET&&w.isStyleUrlResolvable(i.hrefAttr))return null;var o=[],s=[],a=[],l=[],c=[],u=[],d=[],f=[],m=[],y=!1,g=[],_=v.splitNsName(r.toLowerCase())[1],C=_==P;e.attrs.forEach(function(e){var t=n._parseAttr(C,e,o,s,c,u,a,l),r=n._parseInlineTemplateBinding(e,f,d,m);r&&y&&n._reportError("Can't have multiple template bindings on one element. Use only one attribute named 'template' or prefixed with *",e.sourceSpan),t||r||(g.push(n.visitAttribute(e,null)),o.push([e.name,e.value])),r&&(y=!0)});var R=createElementCssSelector(r,o),x=this._parseDirectives(this.selectorMatcher,R),M=[],I=this._createDirectiveAsts(C,e.name,x,s,a,e.sourceSpan,M),A=this._createElementPropertyAsts(e.name,s,I).concat(c),k=t.isTemplateElement||y,O=new b.ProviderElementContext(this.providerViewContext,t.providerContext,k,I,g,M,e.sourceSpan),D=h.visitAll(i.nonBindable?G:this,e.children,H.create(C,I,C?t.providerContext:O));O.afterElement();var N,V=p.isPresent(i.projectAs)?S.CssSelector.parse(i.projectAs)[0]:R,L=t.findNgContentIndex(V);if(i.type===T.PreparsedElementType.NG_CONTENT)p.isPresent(e.children)&&e.children.length>0&&this._reportError(" element cannot have content. must be immediately followed by ",e.sourceSpan),N=new E.NgContentAst((this.ngContentCount++),y?null:L,e.sourceSpan);else if(C)this._assertAllEventsPublishedByDirectives(I,u),this._assertNoComponentsNorElementBindingsOnTemplate(I,A,e.sourceSpan),N=new E.EmbeddedTemplateAst(g,u,M,l,O.transformedDirectiveAsts,O.transformProviders,O.transformedHasViewContainer,D,y?null:L,e.sourceSpan);else{this._assertOnlyOneComponent(I,e.sourceSpan);var F=y?null:t.findNgContentIndex(V);N=new E.ElementAst(r,g,A,u,M,O.transformedDirectiveAsts,O.transformProviders,O.transformedHasViewContainer,D,y?null:F,e.sourceSpan)}if(y){var j=createElementCssSelector(P,f),B=this._parseDirectives(this.selectorMatcher,j),W=this._createDirectiveAsts(!0,e.name,B,d,[],e.sourceSpan,[]),U=this._createElementPropertyAsts(e.name,d,W);this._assertNoComponentsNorElementBindingsOnTemplate(W,U,e.sourceSpan);var z=new b.ProviderElementContext(this.providerViewContext,t.providerContext,t.isTemplateElement,W,[],[],e.sourceSpan);z.afterElement(),N=new E.EmbeddedTemplateAst([],[],[],m,z.transformedDirectiveAsts,z.transformProviders,z.transformedHasViewContainer,[N],L,e.sourceSpan)}return N},TemplateParseVisitor.prototype._parseInlineTemplateBinding=function(e,t,n,r){var i=null;if(this._normalizeAttributeName(e.name)==x)i=e.value;else if(e.name.startsWith(M)){var o=e.name.substring(M.length);i=0==e.value.length?o:o+" "+e.value}if(p.isPresent(i)){for(var s=this._parseTemplateBindings(i,e.sourceSpan),a=0;a elements is deprecated. Use "let-" instead!',t.sourceSpan,g.ParseErrorLevel.WARNING),this._parseVariable(h,c,t.sourceSpan,a)):(this._reportError('"var-" on non